diff --git a/.golangci.yml b/.golangci.yml
index 18b325e206..395a91fe1b 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,7 +1,7 @@
# This file configures github.com/golangci/golangci-lint.
run:
- timeout: 3m
+ timeout: 5m
tests: true
# default is true. Enables skipping of directories:
# vendor$, third_party$, testdata$, examples$, Godeps$, builtin$
diff --git a/.travis.yml b/.travis.yml
index a7db711ccc..197d56748f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,7 +5,7 @@ jobs:
allow_failures:
- stage: build
os: osx
- go: 1.15.x
+ go: 1.17.x
env:
- azure-osx
- azure-ios
@@ -16,7 +16,7 @@ jobs:
- stage: lint
os: linux
dist: bionic
- go: 1.16.x
+ go: 1.17.x
env:
- lint
git:
@@ -31,7 +31,7 @@ jobs:
os: linux
arch: amd64
dist: bionic
- go: 1.16.x
+ go: 1.17.x
env:
- docker
services:
@@ -48,7 +48,7 @@ jobs:
os: linux
arch: arm64
dist: bionic
- go: 1.16.x
+ go: 1.17.x
env:
- docker
services:
@@ -65,7 +65,7 @@ jobs:
if: type = push
os: linux
dist: bionic
- go: 1.16.x
+ go: 1.17.x
env:
- ubuntu-ppa
- GO111MODULE=on
@@ -90,7 +90,7 @@ jobs:
os: linux
dist: bionic
sudo: required
- go: 1.16.x
+ go: 1.17.x
env:
- azure-linux
- GO111MODULE=on
@@ -120,36 +120,6 @@ jobs:
- go run build/ci.go install -dlgo -arch arm64 -cc aarch64-linux-gnu-gcc
- go run build/ci.go archive -arch arm64 -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
- # This builder does the Linux Azure MIPS xgo uploads
- - stage: build
- if: type = push
- os: linux
- dist: bionic
- services:
- - docker
- go: 1.16.x
- env:
- - azure-linux-mips
- - GO111MODULE=on
- git:
- submodules: false # avoid cloning ethereum/tests
- script:
- - go run build/ci.go xgo --alltools -- --targets=linux/mips --ldflags '-extldflags "-static"' -v
- - for bin in build/bin/*-linux-mips; do mv -f "${bin}" "${bin/-linux-mips/}"; done
- - go run build/ci.go archive -arch mips -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
-
- - go run build/ci.go xgo --alltools -- --targets=linux/mipsle --ldflags '-extldflags "-static"' -v
- - for bin in build/bin/*-linux-mipsle; do mv -f "${bin}" "${bin/-linux-mipsle/}"; done
- - go run build/ci.go archive -arch mipsle -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
-
- - go run build/ci.go xgo --alltools -- --targets=linux/mips64 --ldflags '-extldflags "-static"' -v
- - for bin in build/bin/*-linux-mips64; do mv -f "${bin}" "${bin/-linux-mips64/}"; done
- - go run build/ci.go archive -arch mips64 -type tar -signer LINUX_SIGNING_KEY signify SIGNIFY_KEY -upload gethstore/builds
-
- - go run build/ci.go xgo --alltools -- --targets=linux/mips64le --ldflags '-extldflags "-static"' -v
- - for bin in build/bin/*-linux-mips64le; do mv -f "${bin}" "${bin/-linux-mips64le/}"; done
- - go run build/ci.go archive -arch mips64le -type tar -signer LINUX_SIGNING_KEY -signify SIGNIFY_KEY -upload gethstore/builds
-
# This builder does the Android Maven and Azure uploads
- stage: build
if: type = push
@@ -192,7 +162,7 @@ jobs:
- stage: build
if: type = push
os: osx
- go: 1.16.x
+ go: 1.17.x
env:
- azure-osx
- azure-ios
@@ -224,7 +194,7 @@ jobs:
os: linux
arch: amd64
dist: bionic
- go: 1.16.x
+ go: 1.17.x
env:
- GO111MODULE=on
script:
@@ -235,7 +205,7 @@ jobs:
os: linux
arch: arm64
dist: bionic
- go: 1.16.x
+ go: 1.17.x
env:
- GO111MODULE=on
script:
@@ -244,7 +214,7 @@ jobs:
- stage: build
os: linux
dist: bionic
- go: 1.15.x
+ go: 1.16.x
env:
- GO111MODULE=on
script:
@@ -255,7 +225,7 @@ jobs:
if: type = cron
os: linux
dist: bionic
- go: 1.16.x
+ go: 1.17.x
env:
- azure-purge
- GO111MODULE=on
@@ -263,3 +233,15 @@ jobs:
submodules: false # avoid cloning ethereum/tests
script:
- go run build/ci.go purge -store gethstore/builds -days 14
+
+ # This builder executes race tests
+ - stage: build
+ if: type = cron
+ os: linux
+ dist: bionic
+ go: 1.17.x
+ env:
+ - GO111MODULE=on
+ script:
+ - go run build/ci.go test -race -coverage $TEST_PACKAGES
+
diff --git a/Dockerfile b/Dockerfile
index e76c5765bc..7badbc1320 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -4,7 +4,7 @@ ARG VERSION=""
ARG BUILDNUM=""
# Build Geth in a stock Go builder container
-FROM golang:1.16-alpine as builder
+FROM golang:1.17-alpine as builder
RUN apk add --no-cache gcc musl-dev linux-headers git
diff --git a/Dockerfile.alltools b/Dockerfile.alltools
index 71f63b7a47..3ae5377e4f 100644
--- a/Dockerfile.alltools
+++ b/Dockerfile.alltools
@@ -4,7 +4,7 @@ ARG VERSION=""
ARG BUILDNUM=""
# Build Geth in a stock Go builder container
-FROM golang:1.16-alpine as builder
+FROM golang:1.17-alpine as builder
RUN apk add --no-cache gcc musl-dev linux-headers git
diff --git a/Makefile b/Makefile
index cb5a87dad0..9449614739 100644
--- a/Makefile
+++ b/Makefile
@@ -2,11 +2,7 @@
# with Go source code. If you know what GOPATH is then you probably
# don't need to bother with make.
-.PHONY: geth android ios geth-cross evm all test clean
-.PHONY: geth-linux geth-linux-386 geth-linux-amd64 geth-linux-mips64 geth-linux-mips64le
-.PHONY: geth-linux-arm geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-arm64
-.PHONY: geth-darwin geth-darwin-386 geth-darwin-amd64
-.PHONY: geth-windows geth-windows-386 geth-windows-amd64
+.PHONY: geth android ios evm all test clean
GOBIN = ./build/bin
GO ?= latest
@@ -53,95 +49,3 @@ devtools:
env GOBIN= go install ./cmd/abigen
@type "solc" 2> /dev/null || echo 'Please install solc'
@type "protoc" 2> /dev/null || echo 'Please install protoc'
-
-# Cross Compilation Targets (xgo)
-
-geth-cross: geth-linux geth-darwin geth-windows geth-android geth-ios
- @echo "Full cross compilation done:"
- @ls -ld $(GOBIN)/geth-*
-
-geth-linux: geth-linux-386 geth-linux-amd64 geth-linux-arm geth-linux-mips64 geth-linux-mips64le
- @echo "Linux cross compilation done:"
- @ls -ld $(GOBIN)/geth-linux-*
-
-geth-linux-386:
- $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/386 -v ./cmd/geth
- @echo "Linux 386 cross compilation done:"
- @ls -ld $(GOBIN)/geth-linux-* | grep 386
-
-geth-linux-amd64:
- $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/amd64 -v ./cmd/geth
- @echo "Linux amd64 cross compilation done:"
- @ls -ld $(GOBIN)/geth-linux-* | grep amd64
-
-geth-linux-arm: geth-linux-arm-5 geth-linux-arm-6 geth-linux-arm-7 geth-linux-arm64
- @echo "Linux ARM cross compilation done:"
- @ls -ld $(GOBIN)/geth-linux-* | grep arm
-
-geth-linux-arm-5:
- $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/arm-5 -v ./cmd/geth
- @echo "Linux ARMv5 cross compilation done:"
- @ls -ld $(GOBIN)/geth-linux-* | grep arm-5
-
-geth-linux-arm-6:
- $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/arm-6 -v ./cmd/geth
- @echo "Linux ARMv6 cross compilation done:"
- @ls -ld $(GOBIN)/geth-linux-* | grep arm-6
-
-geth-linux-arm-7:
- $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/arm-7 -v ./cmd/geth
- @echo "Linux ARMv7 cross compilation done:"
- @ls -ld $(GOBIN)/geth-linux-* | grep arm-7
-
-geth-linux-arm64:
- $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/arm64 -v ./cmd/geth
- @echo "Linux ARM64 cross compilation done:"
- @ls -ld $(GOBIN)/geth-linux-* | grep arm64
-
-geth-linux-mips:
- $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/mips --ldflags '-extldflags "-static"' -v ./cmd/geth
- @echo "Linux MIPS cross compilation done:"
- @ls -ld $(GOBIN)/geth-linux-* | grep mips
-
-geth-linux-mipsle:
- $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/mipsle --ldflags '-extldflags "-static"' -v ./cmd/geth
- @echo "Linux MIPSle cross compilation done:"
- @ls -ld $(GOBIN)/geth-linux-* | grep mipsle
-
-geth-linux-mips64:
- $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/mips64 --ldflags '-extldflags "-static"' -v ./cmd/geth
- @echo "Linux MIPS64 cross compilation done:"
- @ls -ld $(GOBIN)/geth-linux-* | grep mips64
-
-geth-linux-mips64le:
- $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=linux/mips64le --ldflags '-extldflags "-static"' -v ./cmd/geth
- @echo "Linux MIPS64le cross compilation done:"
- @ls -ld $(GOBIN)/geth-linux-* | grep mips64le
-
-geth-darwin: geth-darwin-386 geth-darwin-amd64
- @echo "Darwin cross compilation done:"
- @ls -ld $(GOBIN)/geth-darwin-*
-
-geth-darwin-386:
- $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=darwin/386 -v ./cmd/geth
- @echo "Darwin 386 cross compilation done:"
- @ls -ld $(GOBIN)/geth-darwin-* | grep 386
-
-geth-darwin-amd64:
- $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=darwin/amd64 -v ./cmd/geth
- @echo "Darwin amd64 cross compilation done:"
- @ls -ld $(GOBIN)/geth-darwin-* | grep amd64
-
-geth-windows: geth-windows-386 geth-windows-amd64
- @echo "Windows cross compilation done:"
- @ls -ld $(GOBIN)/geth-windows-*
-
-geth-windows-386:
- $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=windows/386 -v ./cmd/geth
- @echo "Windows 386 cross compilation done:"
- @ls -ld $(GOBIN)/geth-windows-* | grep 386
-
-geth-windows-amd64:
- $(GORUN) build/ci.go xgo -- --go=$(GO) --targets=windows/amd64 -v ./cmd/geth
- @echo "Windows amd64 cross compilation done:"
- @ls -ld $(GOBIN)/geth-windows-* | grep amd64
diff --git a/README.md b/README.md
index c7adb135d6..f7a7ac5147 100644
--- a/README.md
+++ b/README.md
@@ -68,7 +68,7 @@ This command will:
causing it to download more data in exchange for avoiding processing the entire history
of the Ethereum network, which is very CPU intensive.
* Start up `geth`'s built-in interactive [JavaScript console](https://geth.ethereum.org/docs/interface/javascript-console),
- (via the trailing `console` subcommand) through which you can interact using [`web3` methods](https://web3js.readthedocs.io/en/)
+ (via the trailing `console` subcommand) through which you can interact using [`web3` methods](https://web3js.readthedocs.io/)
(note: the `web3` version bundled within `geth` is very old, and not up to date with official docs),
as well as `geth`'s own [management APIs](https://geth.ethereum.org/docs/rpc/server).
This tool is optional and if you leave it out you can always attach to an already running
@@ -231,7 +231,8 @@ aware of and agree upon. This consists of a small JSON file (e.g. call it `genes
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
- "berlinBlock": 0
+ "berlinBlock": 0,
+ "londonBlock": 0
},
"alloc": {},
"coinbase": "0x0000000000000000000000000000000000000000",
diff --git a/SECURITY.md b/SECURITY.md
index bdce7b8d2a..635c0869fc 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -12,6 +12,8 @@ Audit reports are published in the `docs` folder: https://github.com/ethereum/go
| ------- | ------- | ----------- |
| `geth` | 20170425 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2017-04-25_Geth-audit_Truesec.pdf) |
| `clef` | 20180914 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2018-09-14_Clef-audit_NCC.pdf) |
+| `Discv5` | 20191015 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2019-10-15_Discv5_audit_LeastAuthority.pdf) |
+| `Discv5` | 20200124 | [pdf](https://github.com/ethereum/go-ethereum/blob/master/docs/audits/2020-01-24_DiscV5_audit_Cure53.pdf) |
## Reporting a Vulnerability
diff --git a/accounts/abi/abi.go b/accounts/abi/abi.go
index 5ca7c241db..cd2f4d7978 100644
--- a/accounts/abi/abi.go
+++ b/accounts/abi/abi.go
@@ -34,6 +34,7 @@ type ABI struct {
Constructor Method
Methods map[string]Method
Events map[string]Event
+ Errors map[string]Error
// Additional "special" functions introduced in solidity v0.6.0.
// It's separated from the original default fallback. Each contract
@@ -157,12 +158,13 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
}
abi.Methods = make(map[string]Method)
abi.Events = make(map[string]Event)
+ abi.Errors = make(map[string]Error)
for _, field := range fields {
switch field.Type {
case "constructor":
abi.Constructor = NewMethod("", "", Constructor, field.StateMutability, field.Constant, field.Payable, field.Inputs, nil)
case "function":
- name := abi.overloadedMethodName(field.Name)
+ name := overloadedName(field.Name, func(s string) bool { _, ok := abi.Methods[s]; return ok })
abi.Methods[name] = NewMethod(name, field.Name, Function, field.StateMutability, field.Constant, field.Payable, field.Inputs, field.Outputs)
case "fallback":
// New introduced function type in v0.6.0, check more detail
@@ -182,8 +184,10 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
}
abi.Receive = NewMethod("", "", Receive, field.StateMutability, field.Constant, field.Payable, nil, nil)
case "event":
- name := abi.overloadedEventName(field.Name)
+ name := overloadedName(field.Name, func(s string) bool { _, ok := abi.Events[s]; return ok })
abi.Events[name] = NewEvent(name, field.Name, field.Anonymous, field.Inputs)
+ case "error":
+ abi.Errors[field.Name] = NewError(field.Name, field.Inputs)
default:
return fmt.Errorf("abi: could not recognize type %v of field %v", field.Type, field.Name)
}
@@ -191,36 +195,6 @@ func (abi *ABI) UnmarshalJSON(data []byte) error {
return nil
}
-// overloadedMethodName returns the next available name for a given function.
-// Needed since solidity allows for function overload.
-//
-// e.g. if the abi contains Methods send, send1
-// overloadedMethodName would return send2 for input send.
-func (abi *ABI) overloadedMethodName(rawName string) string {
- name := rawName
- _, ok := abi.Methods[name]
- for idx := 0; ok; idx++ {
- name = fmt.Sprintf("%s%d", rawName, idx)
- _, ok = abi.Methods[name]
- }
- return name
-}
-
-// overloadedEventName returns the next available name for a given event.
-// Needed since solidity allows for event overload.
-//
-// e.g. if the abi contains events received, received1
-// overloadedEventName would return received2 for input received.
-func (abi *ABI) overloadedEventName(rawName string) string {
- name := rawName
- _, ok := abi.Events[name]
- for idx := 0; ok; idx++ {
- name = fmt.Sprintf("%s%d", rawName, idx)
- _, ok = abi.Events[name]
- }
- return name
-}
-
// MethodById looks up a method by the 4-byte id,
// returns nil if none found.
func (abi *ABI) MethodById(sigdata []byte) (*Method, error) {
@@ -277,3 +251,20 @@ func UnpackRevert(data []byte) (string, error) {
}
return unpacked[0].(string), nil
}
+
+// overloadedName returns the next available name for a given thing.
+// Needed since solidity allows for overloading.
+//
+// e.g. if the abi contains Methods send, send1
+// overloadedName would return send2 for input send.
+//
+// overloadedName works for methods, events and errors.
+func overloadedName(rawName string, isAvail func(string) bool) string {
+ name := rawName
+ ok := isAvail(name)
+ for idx := 0; ok; idx++ {
+ name = fmt.Sprintf("%s%d", rawName, idx)
+ ok = isAvail(name)
+ }
+ return name
+}
diff --git a/accounts/abi/abi_test.go b/accounts/abi/abi_test.go
index a022ec5f9d..cc8dfc61c3 100644
--- a/accounts/abi/abi_test.go
+++ b/accounts/abi/abi_test.go
@@ -295,6 +295,20 @@ func TestOverloadedMethodSignature(t *testing.T) {
check("bar0", "bar(uint256,uint256)", false)
}
+func TestCustomErrors(t *testing.T) {
+ json := `[{ "inputs": [ { "internalType": "uint256", "name": "", "type": "uint256" } ],"name": "MyError", "type": "error"} ]`
+ abi, err := JSON(strings.NewReader(json))
+ if err != nil {
+ t.Fatal(err)
+ }
+ check := func(name string, expect string) {
+ if abi.Errors[name].Sig != expect {
+ t.Fatalf("The signature of overloaded method mismatch, want %s, have %s", expect, abi.Methods[name].Sig)
+ }
+ }
+ check("MyError", "MyError(uint256)")
+}
+
func TestMultiPack(t *testing.T) {
abi, err := JSON(strings.NewReader(jsondata))
if err != nil {
diff --git a/accounts/abi/argument.go b/accounts/abi/argument.go
index e6d5245596..261b4d1b86 100644
--- a/accounts/abi/argument.go
+++ b/accounts/abi/argument.go
@@ -137,7 +137,7 @@ func (arguments Arguments) copyAtomic(v interface{}, marshalledValues interface{
dst := reflect.ValueOf(v).Elem()
src := reflect.ValueOf(marshalledValues)
- if dst.Kind() == reflect.Struct && src.Kind() != reflect.Struct {
+ if dst.Kind() == reflect.Struct {
return set(dst.Field(0), src)
}
return set(dst, src)
diff --git a/accounts/abi/bind/backends/simulated.go b/accounts/abi/bind/backends/simulated.go
index e410522ac8..6854c9624e 100644
--- a/accounts/abi/bind/backends/simulated.go
+++ b/accounts/abi/bind/backends/simulated.go
@@ -462,6 +462,9 @@ func (b *SimulatedBackend) PendingNonceAt(ctx context.Context, account common.Ad
// SuggestGasPrice implements ContractTransactor.SuggestGasPrice. Since the simulated
// 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
}
diff --git a/accounts/abi/bind/backends/simulated_test.go b/accounts/abi/bind/backends/simulated_test.go
index 6132678107..4e63e3efff 100644
--- a/accounts/abi/bind/backends/simulated_test.go
+++ b/accounts/abi/bind/backends/simulated_test.go
@@ -916,8 +916,8 @@ func TestSuggestGasPrice(t *testing.T) {
if err != nil {
t.Errorf("could not get gas price: %v", err)
}
- if gasPrice.Uint64() != uint64(1) {
- t.Errorf("gas price was not expected value of 1. actual: %v", gasPrice.Uint64())
+ if gasPrice.Uint64() != sim.pendingBlock.Header().BaseFee.Uint64() {
+ t.Errorf("gas price was not expected value of %v. actual: %v", sim.pendingBlock.Header().BaseFee.Uint64(), gasPrice.Uint64())
}
}
diff --git a/accounts/abi/bind/base.go b/accounts/abi/bind/base.go
index 354632b257..f4e5a2a900 100644
--- a/accounts/abi/bind/base.go
+++ b/accounts/abi/bind/base.go
@@ -231,108 +231,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 reasonable gas price values
- if opts.GasPrice != nil && (opts.GasFeeCap != nil || opts.GasTipCap != nil) {
- return nil, errors.New("both gasPrice and (maxFeePerGas or maxPriorityFeePerGas) specified")
+ // Estimate FeeCap
+ gasFeeCap := opts.GasFeeCap
+ if gasFeeCap == nil {
+ gasFeeCap = new(big.Int).Add(
+ gasTipCap,
+ new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
+ )
}
- head, err := c.transactor.HeaderByNumber(ensureContext(opts.Context), nil)
+ 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
}
- if head.BaseFee != nil && opts.GasPrice == nil {
- if opts.GasTipCap == nil {
- tip, err := c.transactor.SuggestGasTipCap(ensureContext(opts.Context))
- if err != nil {
- return nil, err
- }
- opts.GasTipCap = tip
- }
- if opts.GasFeeCap == nil {
- gasFeeCap := new(big.Int).Add(
- opts.GasTipCap,
- new(big.Int).Mul(head.BaseFee, big.NewInt(2)),
- )
- opts.GasFeeCap = gasFeeCap
- }
- if opts.GasFeeCap.Cmp(opts.GasTipCap) < 0 {
- return nil, fmt.Errorf("maxFeePerGas (%v) < maxPriorityFeePerGas (%v)", opts.GasFeeCap, opts.GasTipCap)
- }
- } else {
- if opts.GasFeeCap != nil || opts.GasTipCap != nil {
- return nil, errors.New("maxFeePerGas or maxPriorityFeePerGas specified but london is not active yet")
- }
- if opts.GasPrice == nil {
- price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
- if err != nil {
- return nil, err
- }
- opts.GasPrice = price
- }
+ baseTx := &types.DynamicFeeTx{
+ To: contract,
+ Nonce: nonce,
+ GasFeeCap: gasFeeCap,
+ GasTipCap: gasTipCap,
+ Gas: gasLimit,
+ Value: value,
+ Data: input,
}
- 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 := ethereum.CallMsg{From: opts.From, To: contract, GasPrice: opts.GasPrice, GasTipCap: opts.GasTipCap, GasFeeCap: opts.GasFeeCap, Value: value, Data: input}
- gasLimit, err = c.transactor.EstimateGas(ensureContext(opts.Context), msg)
+ 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 london is not active yet")
+ }
+ // Normalize value
+ value := opts.Value
+ if value == nil {
+ value = new(big.Int)
+ }
+ // Estimate GasPrice
+ gasPrice := opts.GasPrice
+ if gasPrice == nil {
+ price, err := c.transactor.SuggestGasPrice(ensureContext(opts.Context))
if err != nil {
- return nil, fmt.Errorf("failed to estimate gas needed: %v", err)
+ return nil, err
+ }
+ gasPrice = price
+ }
+ // Estimate GasLimit
+ gasLimit := opts.GasLimit
+ if opts.GasLimit == 0 {
+ var err error
+ gasLimit, err = c.estimateGasLimit(opts, contract, input, gasPrice, nil, nil, value)
+ if err != nil {
+ return nil, err
}
}
- // Create the transaction, sign it and schedule it for execution
- var rawTx *types.Transaction
- if opts.GasFeeCap == nil {
- baseTx := &types.LegacyTx{
- Nonce: nonce,
- GasPrice: opts.GasPrice,
- Gas: gasLimit,
- Value: value,
- Data: 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
}
- if contract != nil {
- baseTx.To = &c.address
- }
- rawTx = types.NewTx(baseTx)
+ }
+ msg := ethereum.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 {
- baseTx := &types.DynamicFeeTx{
- Nonce: nonce,
- GasFeeCap: opts.GasFeeCap,
- GasTipCap: opts.GasTipCap,
- Gas: gasLimit,
- Value: value,
- Data: input,
- }
- if contract != nil {
- baseTx.To = &c.address
- }
- rawTx = types.NewTx(baseTx)
+ 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")
}
@@ -431,6 +481,9 @@ func (c *BoundContract) WatchLogs(opts *WatchOpts, name string, query ...[]inter
// UnpackLog unpacks a retrieved log into the provided output structure.
func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log) error {
+ if log.Topics[0] != c.abi.Events[event].ID {
+ return fmt.Errorf("event signature mismatch")
+ }
if len(log.Data) > 0 {
if err := c.abi.UnpackIntoInterface(out, event, log.Data); err != nil {
return err
@@ -447,6 +500,9 @@ func (c *BoundContract) UnpackLog(out interface{}, event string, log types.Log)
// UnpackLogIntoMap unpacks a retrieved log into the provided map.
func (c *BoundContract) UnpackLogIntoMap(out map[string]interface{}, event string, log types.Log) error {
+ if log.Topics[0] != c.abi.Events[event].ID {
+ return fmt.Errorf("event signature mismatch")
+ }
if len(log.Data) > 0 {
if err := c.abi.UnpackIntoMap(out, event, log.Data); err != nil {
return err
diff --git a/accounts/abi/bind/base_test.go b/accounts/abi/bind/base_test.go
index c4740f68b7..08ba18f95e 100644
--- a/accounts/abi/bind/base_test.go
+++ b/accounts/abi/bind/base_test.go
@@ -31,8 +31,49 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/rlp"
+ "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 ethereum.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
@@ -110,7 +151,7 @@ const hexData = "0x000000000000000000000000376c47978271565f56deb45495afa69e59c16
func TestUnpackIndexedStringTyLogIntoMap(t *testing.T) {
hash := crypto.Keccak256Hash([]byte("testName"))
topics := []common.Hash{
- common.HexToHash("0x0"),
+ crypto.Keccak256Hash([]byte("received(string,address,uint256,bytes)")),
hash,
}
mockLog := newMockLog(topics, common.HexToHash("0x0"))
@@ -135,7 +176,7 @@ func TestUnpackIndexedSliceTyLogIntoMap(t *testing.T) {
}
hash := crypto.Keccak256Hash(sliceBytes)
topics := []common.Hash{
- common.HexToHash("0x0"),
+ crypto.Keccak256Hash([]byte("received(string[],address,uint256,bytes)")),
hash,
}
mockLog := newMockLog(topics, common.HexToHash("0x0"))
@@ -160,7 +201,7 @@ func TestUnpackIndexedArrayTyLogIntoMap(t *testing.T) {
}
hash := crypto.Keccak256Hash(arrBytes)
topics := []common.Hash{
- common.HexToHash("0x0"),
+ crypto.Keccak256Hash([]byte("received(address[2],address,uint256,bytes)")),
hash,
}
mockLog := newMockLog(topics, common.HexToHash("0x0"))
@@ -187,7 +228,7 @@ func TestUnpackIndexedFuncTyLogIntoMap(t *testing.T) {
var functionTy [24]byte
copy(functionTy[:], functionTyBytes[0:24])
topics := []common.Hash{
- common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
+ crypto.Keccak256Hash([]byte("received(function,address,uint256,bytes)")),
common.BytesToHash(functionTyBytes),
}
mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
@@ -208,7 +249,7 @@ func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
bytes := []byte{1, 2, 3, 4, 5}
hash := crypto.Keccak256Hash(bytes)
topics := []common.Hash{
- common.HexToHash("0x99b5620489b6ef926d4518936cfec15d305452712b88bd59da2d9c10fb0953e8"),
+ crypto.Keccak256Hash([]byte("received(bytes,address,uint256,bytes)")),
hash,
}
mockLog := newMockLog(topics, common.HexToHash("0x5c698f13940a2153440c6d19660878bc90219d9298fdcf37365aa8d88d40fc42"))
@@ -226,6 +267,51 @@ func TestUnpackIndexedBytesTyLogIntoMap(t *testing.T) {
unpackAndCheck(t, bc, expectedReceivedMap, mockLog)
}
+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)
+}
+
func unpackAndCheck(t *testing.T, bc *bind.BoundContract, expected map[string]interface{}, mockLog types.Log) {
received := make(map[string]interface{})
if err := bc.UnpackLogIntoMap(received, "received", mockLog); err != nil {
diff --git a/accounts/abi/bind/bind_test.go b/accounts/abi/bind/bind_test.go
index d49b436db6..5a436607ce 100644
--- a/accounts/abi/bind/bind_test.go
+++ b/accounts/abi/bind/bind_test.go
@@ -1785,6 +1785,132 @@ var bindTests = []struct {
nil,
nil,
},
+ // Test resolving single struct argument
+ {
+ `NewSingleStructArgument`,
+ `
+ pragma solidity ^0.8.0;
+
+ contract NewSingleStructArgument {
+ struct MyStruct{
+ uint256 a;
+ uint256 b;
+ }
+ event StructEvent(MyStruct s);
+ function TestEvent() public {
+ emit StructEvent(MyStruct({a: 1, b: 2}));
+ }
+ }
+ `,
+ []string{"608060405234801561001057600080fd5b50610113806100206000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c806324ec1d3f14602d575b600080fd5b60336035565b005b7fb4b2ff75e30cb4317eaae16dd8a187dd89978df17565104caa6c2797caae27d460405180604001604052806001815260200160028152506040516078919060ba565b60405180910390a1565b6040820160008201516096600085018260ad565b50602082015160a7602085018260ad565b50505050565b60b48160d3565b82525050565b600060408201905060cd60008301846082565b92915050565b600081905091905056fea26469706673582212208823628796125bf9941ce4eda18da1be3cf2931b231708ab848e1bd7151c0c9a64736f6c63430008070033"},
+ []string{`[{"anonymous":false,"inputs":[{"components":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"b","type":"uint256"}],"indexed":false,"internalType":"struct Test.MyStruct","name":"s","type":"tuple"}],"name":"StructEvent","type":"event"},{"inputs":[],"name":"TestEvent","outputs":[],"stateMutability":"nonpayable","type":"function"}]`},
+ `
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
+ "github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/eth/ethconfig"
+ `,
+ `
+ var (
+ key, _ = crypto.GenerateKey()
+ user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
+ sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
+ )
+ defer sim.Close()
+
+ _, _, d, err := DeployNewSingleStructArgument(user, sim)
+ if err != nil {
+ t.Fatalf("Failed to deploy contract %v", err)
+ }
+ sim.Commit()
+
+ _, err = d.TestEvent(user)
+ if err != nil {
+ t.Fatalf("Failed to call contract %v", err)
+ }
+ sim.Commit()
+
+ it, err := d.FilterStructEvent(nil)
+ if err != nil {
+ t.Fatalf("Failed to filter contract event %v", err)
+ }
+ var count int
+ for it.Next() {
+ if it.Event.S.A.Cmp(big.NewInt(1)) != 0 {
+ t.Fatal("Unexpected contract event")
+ }
+ if it.Event.S.B.Cmp(big.NewInt(2)) != 0 {
+ t.Fatal("Unexpected contract event")
+ }
+ count += 1
+ }
+ if count != 1 {
+ t.Fatal("Unexpected contract event number")
+ }
+ `,
+ nil,
+ nil,
+ nil,
+ nil,
+ },
+ // Test errors introduced in v0.8.4
+ {
+ `NewErrors`,
+ `
+ pragma solidity >0.8.4;
+
+ contract NewErrors {
+ error MyError(uint256);
+ error MyError1(uint256);
+ error MyError2(uint256, uint256);
+ error MyError3(uint256 a, uint256 b, uint256 c);
+ function Error() public pure {
+ revert MyError3(1,2,3);
+ }
+ }
+ `,
+ []string{"0x6080604052348015600f57600080fd5b5060998061001e6000396000f3fe6080604052348015600f57600080fd5b506004361060285760003560e01c8063726c638214602d575b600080fd5b60336035565b005b60405163024876cd60e61b815260016004820152600260248201526003604482015260640160405180910390fdfea264697066735822122093f786a1bc60216540cd999fbb4a6109e0fef20abcff6e9107fb2817ca968f3c64736f6c63430008070033"},
+ []string{`[{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError1","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"MyError2","type":"error"},{"inputs":[{"internalType":"uint256","name":"a","type":"uint256"},{"internalType":"uint256","name":"b","type":"uint256"},{"internalType":"uint256","name":"c","type":"uint256"}],"name":"MyError3","type":"error"},{"inputs":[],"name":"Error","outputs":[],"stateMutability":"pure","type":"function"}]`},
+ `
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/accounts/abi/bind"
+ "github.com/ethereum/go-ethereum/accounts/abi/bind/backends"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/eth/ethconfig"
+ `,
+ `
+ var (
+ key, _ = crypto.GenerateKey()
+ user, _ = bind.NewKeyedTransactorWithChainID(key, big.NewInt(1337))
+ sim = backends.NewSimulatedBackend(core.GenesisAlloc{user.From: {Balance: big.NewInt(1000000000000000000)}}, ethconfig.Defaults.Miner.GasCeil)
+ )
+ defer sim.Close()
+
+ _, tx, contract, err := DeployNewErrors(user, sim)
+ if err != nil {
+ t.Fatal(err)
+ }
+ sim.Commit()
+ _, err = bind.WaitDeployed(nil, sim, tx)
+ if err != nil {
+ t.Error(err)
+ }
+ if err := contract.Error(new(bind.CallOpts)); err == nil {
+ t.Fatalf("expected contract to throw error")
+ }
+ // TODO (MariusVanDerWijden unpack error using abigen
+ // once that is implemented
+ `,
+ nil,
+ nil,
+ nil,
+ nil,
+ },
}
// Tests that packages generated by the binder can be successfully compiled and
diff --git a/accounts/abi/error.go b/accounts/abi/error.go
index f0f71b6c91..3d1a4877dd 100644
--- a/accounts/abi/error.go
+++ b/accounts/abi/error.go
@@ -1,4 +1,4 @@
-// Copyright 2016 The go-ethereum Authors
+// 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
@@ -17,66 +17,75 @@
package abi
import (
+ "bytes"
"errors"
"fmt"
- "reflect"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/crypto"
)
-var (
- errBadBool = errors.New("abi: improperly encoded boolean value")
-)
-
-// formatSliceString formats the reflection kind with the given slice size
-// and returns a formatted string representation.
-func formatSliceString(kind reflect.Kind, sliceSize int) string {
- if sliceSize == -1 {
- return fmt.Sprintf("[]%v", kind)
- }
- return fmt.Sprintf("[%d]%v", sliceSize, kind)
+type Error struct {
+ Name string
+ Inputs Arguments
+ str string
+ // Sig contains the string signature according to the ABI spec.
+ // e.g. event foo(uint32 a, int b) = "foo(uint32,int256)"
+ // Please note that "int" is substitute for its canonical representation "int256"
+ Sig string
+ // ID returns the canonical representation of the event's signature used by the
+ // abi definition to identify event names and types.
+ ID common.Hash
}
-// sliceTypeCheck checks that the given slice can by assigned to the reflection
-// type in t.
-func sliceTypeCheck(t Type, val reflect.Value) error {
- if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
- return typeErr(formatSliceString(t.GetType().Kind(), t.Size), val.Type())
- }
-
- if t.T == ArrayTy && val.Len() != t.Size {
- return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
- }
-
- if t.Elem.T == SliceTy || t.Elem.T == ArrayTy {
- if val.Len() > 0 {
- return sliceTypeCheck(*t.Elem, val.Index(0))
+func NewError(name string, inputs Arguments) Error {
+ // sanitize inputs to remove inputs without names
+ // and precompute string and sig representation.
+ names := make([]string, len(inputs))
+ types := make([]string, len(inputs))
+ for i, input := range inputs {
+ if input.Name == "" {
+ inputs[i] = Argument{
+ Name: fmt.Sprintf("arg%d", i),
+ Indexed: input.Indexed,
+ Type: input.Type,
+ }
+ } else {
+ inputs[i] = input
}
+ // string representation
+ names[i] = fmt.Sprintf("%v %v", input.Type, inputs[i].Name)
+ if input.Indexed {
+ names[i] = fmt.Sprintf("%v indexed %v", input.Type, inputs[i].Name)
+ }
+ // sig representation
+ types[i] = input.Type.String()
}
- if val.Type().Elem().Kind() != t.Elem.GetType().Kind() {
- return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type())
+ str := fmt.Sprintf("error %v(%v)", name, strings.Join(names, ", "))
+ sig := fmt.Sprintf("%v(%v)", name, strings.Join(types, ","))
+ id := common.BytesToHash(crypto.Keccak256([]byte(sig)))
+
+ return Error{
+ Name: name,
+ Inputs: inputs,
+ str: str,
+ Sig: sig,
+ ID: id,
}
- return nil
}
-// typeCheck checks that the given reflection value can be assigned to the reflection
-// type in t.
-func typeCheck(t Type, value reflect.Value) error {
- if t.T == SliceTy || t.T == ArrayTy {
- return sliceTypeCheck(t, value)
- }
-
- // Check base type validity. Element types will be checked later on.
- if t.GetType().Kind() != value.Kind() {
- return typeErr(t.GetType().Kind(), value.Kind())
- } else if t.T == FixedBytesTy && t.Size != value.Len() {
- return typeErr(t.GetType(), value.Type())
- } else {
- return nil
- }
-
+func (e *Error) String() string {
+ return e.str
}
-// typeErr returns a formatted type casting error.
-func typeErr(expected, got interface{}) error {
- return fmt.Errorf("abi: cannot use %v as type %v as argument", got, expected)
+func (e *Error) Unpack(data []byte) (interface{}, error) {
+ if len(data) < 4 {
+ return "", errors.New("invalid data for unpacking")
+ }
+ if !bytes.Equal(data[:4], e.ID[:4]) {
+ return "", errors.New("invalid data for unpacking")
+ }
+ return e.Inputs.Unpack(data[4:])
}
diff --git a/accounts/abi/error_handling.go b/accounts/abi/error_handling.go
new file mode 100644
index 0000000000..f0f71b6c91
--- /dev/null
+++ b/accounts/abi/error_handling.go
@@ -0,0 +1,82 @@
+// 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 abi
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+)
+
+var (
+ errBadBool = errors.New("abi: improperly encoded boolean value")
+)
+
+// formatSliceString formats the reflection kind with the given slice size
+// and returns a formatted string representation.
+func formatSliceString(kind reflect.Kind, sliceSize int) string {
+ if sliceSize == -1 {
+ return fmt.Sprintf("[]%v", kind)
+ }
+ return fmt.Sprintf("[%d]%v", sliceSize, kind)
+}
+
+// sliceTypeCheck checks that the given slice can by assigned to the reflection
+// type in t.
+func sliceTypeCheck(t Type, val reflect.Value) error {
+ if val.Kind() != reflect.Slice && val.Kind() != reflect.Array {
+ return typeErr(formatSliceString(t.GetType().Kind(), t.Size), val.Type())
+ }
+
+ if t.T == ArrayTy && val.Len() != t.Size {
+ return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), formatSliceString(val.Type().Elem().Kind(), val.Len()))
+ }
+
+ if t.Elem.T == SliceTy || t.Elem.T == ArrayTy {
+ if val.Len() > 0 {
+ return sliceTypeCheck(*t.Elem, val.Index(0))
+ }
+ }
+
+ if val.Type().Elem().Kind() != t.Elem.GetType().Kind() {
+ return typeErr(formatSliceString(t.Elem.GetType().Kind(), t.Size), val.Type())
+ }
+ return nil
+}
+
+// typeCheck checks that the given reflection value can be assigned to the reflection
+// type in t.
+func typeCheck(t Type, value reflect.Value) error {
+ if t.T == SliceTy || t.T == ArrayTy {
+ return sliceTypeCheck(t, value)
+ }
+
+ // Check base type validity. Element types will be checked later on.
+ if t.GetType().Kind() != value.Kind() {
+ return typeErr(t.GetType().Kind(), value.Kind())
+ } else if t.T == FixedBytesTy && t.Size != value.Len() {
+ return typeErr(t.GetType(), value.Type())
+ } else {
+ return nil
+ }
+
+}
+
+// typeErr returns a formatted type casting error.
+func typeErr(expected, got interface{}) error {
+ return fmt.Errorf("abi: cannot use %v as type %v as argument", got, expected)
+}
diff --git a/accounts/abi/reflect.go b/accounts/abi/reflect.go
index 11248e0730..35e5556d2c 100644
--- a/accounts/abi/reflect.go
+++ b/accounts/abi/reflect.go
@@ -123,15 +123,8 @@ func set(dst, src reflect.Value) error {
func setSlice(dst, src reflect.Value) error {
slice := reflect.MakeSlice(dst.Type(), src.Len(), src.Len())
for i := 0; i < src.Len(); i++ {
- if src.Index(i).Kind() == reflect.Struct {
- if err := set(slice.Index(i), src.Index(i)); err != nil {
- return err
- }
- } else {
- // e.g. [][32]uint8 to []common.Hash
- if err := set(slice.Index(i), src.Index(i)); err != nil {
- return err
- }
+ if err := set(slice.Index(i), src.Index(i)); err != nil {
+ return err
}
}
if dst.CanSet() {
diff --git a/accounts/abi/unpack_test.go b/accounts/abi/unpack_test.go
index b88f77805b..e617f8abc5 100644
--- a/accounts/abi/unpack_test.go
+++ b/accounts/abi/unpack_test.go
@@ -762,20 +762,24 @@ func TestUnpackTuple(t *testing.T) {
buff.Write(common.Hex2Bytes("ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff")) // ret[b] = -1
// If the result is single tuple, use struct as return value container directly.
- v := struct {
+ type v struct {
A *big.Int
B *big.Int
- }{new(big.Int), new(big.Int)}
+ }
+ type r struct {
+ Result v
+ }
+ var ret0 = new(r)
+ err = abi.UnpackIntoInterface(ret0, "tuple", buff.Bytes())
- err = abi.UnpackIntoInterface(&v, "tuple", buff.Bytes())
if err != nil {
t.Error(err)
} else {
- if v.A.Cmp(big.NewInt(1)) != 0 {
- t.Errorf("unexpected value unpacked: want %x, got %x", 1, v.A)
+ if ret0.Result.A.Cmp(big.NewInt(1)) != 0 {
+ t.Errorf("unexpected value unpacked: want %x, got %x", 1, ret0.Result.A)
}
- if v.B.Cmp(big.NewInt(-1)) != 0 {
- t.Errorf("unexpected value unpacked: want %x, got %x", -1, v.B)
+ if ret0.Result.B.Cmp(big.NewInt(-1)) != 0 {
+ t.Errorf("unexpected value unpacked: want %x, got %x", -1, ret0.Result.B)
}
}
diff --git a/accounts/keystore/account_cache_test.go b/accounts/keystore/account_cache_test.go
index fe9233c046..a847545bc8 100644
--- a/accounts/keystore/account_cache_test.go
+++ b/accounts/keystore/account_cache_test.go
@@ -96,7 +96,7 @@ func TestWatchNoDir(t *testing.T) {
// Create ks but not the directory that it watches.
rand.Seed(time.Now().UnixNano())
- dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
+ dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watchnodir-test-%d-%d", os.Getpid(), rand.Int()))
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
list := ks.Accounts()
@@ -322,7 +322,7 @@ func TestUpdatedKeyfileContents(t *testing.T) {
// Create a temporary kesytore to test with
rand.Seed(time.Now().UnixNano())
- dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-watch-test-%d-%d", os.Getpid(), rand.Int()))
+ dir := filepath.Join(os.TempDir(), fmt.Sprintf("eth-keystore-updatedkeyfilecontents-test-%d-%d", os.Getpid(), rand.Int()))
ks := NewKeyStore(dir, LightScryptN, LightScryptP)
list := ks.Accounts()
diff --git a/accounts/keystore/watch.go b/accounts/keystore/watch.go
index d6ef53327d..ad176040d6 100644
--- a/accounts/keystore/watch.go
+++ b/accounts/keystore/watch.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 (darwin && !ios && cgo) || freebsd || (linux && !arm64) || netbsd || solaris
// +build darwin,!ios,cgo freebsd linux,!arm64 netbsd solaris
package keystore
diff --git a/accounts/keystore/watch_fallback.go b/accounts/keystore/watch_fallback.go
index de0e87f8a5..e40eca42fe 100644
--- a/accounts/keystore/watch_fallback.go
+++ b/accounts/keystore/watch_fallback.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 (darwin && !cgo) || ios || (linux && arm64) || windows || (!darwin && !freebsd && !linux && !netbsd && !solaris)
// +build darwin,!cgo ios linux,arm64 windows !darwin,!freebsd,!linux,!netbsd,!solaris
// This is the fallback implementation of directory watching.
diff --git a/accounts/manager.go b/accounts/manager.go
index acf41ed8e9..1e111d1948 100644
--- a/accounts/manager.go
+++ b/accounts/manager.go
@@ -25,6 +25,10 @@ import (
"github.com/ethereum/go-ethereum/event"
)
+// managerSubBufferSize determines how many incoming wallet events
+// the manager will buffer in its channel.
+const managerSubBufferSize = 50
+
// Config contains the settings of the global account manager.
//
// TODO(rjl493456442, karalabe, holiman): Get rid of this when account management
@@ -33,18 +37,27 @@ type Config struct {
InsecureUnlockAllowed bool // Whether account unlocking in insecure environment is allowed
}
+// newBackendEvent lets the manager know it should
+// track the given backend for wallet updates.
+type newBackendEvent struct {
+ backend Backend
+ processed chan struct{} // Informs event emitter that backend has been integrated
+}
+
// Manager is an overarching account manager that can communicate with various
// backends for signing transactions.
type Manager struct {
- config *Config // Global account manager configurations
- backends map[reflect.Type][]Backend // Index of backends currently registered
- updaters []event.Subscription // Wallet update subscriptions for all backends
- updates chan WalletEvent // Subscription sink for backend wallet changes
- wallets []Wallet // Cache of all wallets from all registered backends
+ config *Config // Global account manager configurations
+ backends map[reflect.Type][]Backend // Index of backends currently registered
+ updaters []event.Subscription // Wallet update subscriptions for all backends
+ updates chan WalletEvent // Subscription sink for backend wallet changes
+ newBackends chan newBackendEvent // Incoming backends to be tracked by the manager
+ wallets []Wallet // Cache of all wallets from all registered backends
feed event.Feed // Wallet feed notifying of arrivals/departures
quit chan chan error
+ term chan struct{} // Channel is closed upon termination of the update loop
lock sync.RWMutex
}
@@ -57,7 +70,7 @@ func NewManager(config *Config, backends ...Backend) *Manager {
wallets = merge(wallets, backend.Wallets()...)
}
// Subscribe to wallet notifications from all backends
- updates := make(chan WalletEvent, 4*len(backends))
+ updates := make(chan WalletEvent, managerSubBufferSize)
subs := make([]event.Subscription, len(backends))
for i, backend := range backends {
@@ -65,12 +78,14 @@ func NewManager(config *Config, backends ...Backend) *Manager {
}
// Assemble the account manager and return
am := &Manager{
- config: config,
- backends: make(map[reflect.Type][]Backend),
- updaters: subs,
- updates: updates,
- wallets: wallets,
- quit: make(chan chan error),
+ config: config,
+ backends: make(map[reflect.Type][]Backend),
+ updaters: subs,
+ updates: updates,
+ newBackends: make(chan newBackendEvent),
+ wallets: wallets,
+ quit: make(chan chan error),
+ term: make(chan struct{}),
}
for _, backend := range backends {
kind := reflect.TypeOf(backend)
@@ -93,6 +108,14 @@ func (am *Manager) Config() *Config {
return am.config
}
+// AddBackend starts the tracking of an additional backend for wallet updates.
+// cmd/geth assumes once this func returns the backends have been already integrated.
+func (am *Manager) AddBackend(backend Backend) {
+ done := make(chan struct{})
+ am.newBackends <- newBackendEvent{backend, done}
+ <-done
+}
+
// update is the wallet event loop listening for notifications from the backends
// and updating the cache of wallets.
func (am *Manager) update() {
@@ -122,10 +145,22 @@ func (am *Manager) update() {
// Notify any listeners of the event
am.feed.Send(event)
-
+ case event := <-am.newBackends:
+ am.lock.Lock()
+ // Update caches
+ backend := event.backend
+ am.wallets = merge(am.wallets, backend.Wallets()...)
+ am.updaters = append(am.updaters, backend.Subscribe(am.updates))
+ kind := reflect.TypeOf(backend)
+ am.backends[kind] = append(am.backends[kind], backend)
+ am.lock.Unlock()
+ close(event.processed)
case errc := <-am.quit:
// Manager terminating, return
errc <- nil
+ // Signals event emitters the loop is not receiving values
+ // to prevent them from getting stuck.
+ close(am.term)
return
}
}
@@ -133,6 +168,9 @@ func (am *Manager) update() {
// Backends retrieves the backend(s) with the given type from the account manager.
func (am *Manager) Backends(kind reflect.Type) []Backend {
+ am.lock.RLock()
+ defer am.lock.RUnlock()
+
return am.backends[kind]
}
diff --git a/appveyor.yml b/appveyor.yml
index a72163382a..65b5f96841 100644
--- a/appveyor.yml
+++ b/appveyor.yml
@@ -1,29 +1,57 @@
-os: Visual Studio 2019
clone_depth: 5
version: "{branch}.{build}"
+
+image:
+ - Ubuntu
+ - Visual Studio 2019
+
environment:
matrix:
- # We use gcc from MSYS2 because it is the most recent compiler version available on
- # AppVeyor. Note: gcc.exe only works properly if the corresponding bin/ directory is
- # contained in PATH.
- GETH_ARCH: amd64
- GETH_CC: C:\msys64\mingw64\bin\gcc.exe
- PATH: C:\msys64\mingw64\bin;C:\Program Files (x86)\NSIS\;%PATH%
+ GETH_MINGW: 'C:\msys64\mingw64'
- GETH_ARCH: 386
- GETH_CC: C:\msys64\mingw32\bin\gcc.exe
- PATH: C:\msys64\mingw32\bin;C:\Program Files (x86)\NSIS\;%PATH%
+ GETH_MINGW: 'C:\msys64\mingw32'
install:
- git submodule update --init --depth 1
- go version
- - "%GETH_CC% --version"
-build_script:
- - go run build\ci.go install -dlgo -arch %GETH_ARCH% -cc %GETH_CC%
+for:
+ # Linux has its own script without -arch and -cc.
+ # The linux builder also runs lint.
+ - matrix:
+ only:
+ - image: Ubuntu
+ build_script:
+ - go run build/ci.go lint
+ - go run build/ci.go install -dlgo
+ test_script:
+ - go run build/ci.go test -dlgo -coverage
-after_build:
- - go run build\ci.go archive -arch %GETH_ARCH% -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
- - go run build\ci.go nsis -arch %GETH_ARCH% -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
+ # linux/386 is disabled.
+ - matrix:
+ exclude:
+ - image: Ubuntu
+ GETH_ARCH: 386
-test_script:
- - go run build\ci.go test -dlgo -arch %GETH_ARCH% -cc %GETH_CC% -coverage
+ # Windows builds for amd64 + 386.
+ - matrix:
+ only:
+ - image: Visual Studio 2019
+ environment:
+ # We use gcc from MSYS2 because it is the most recent compiler version available on
+ # AppVeyor. Note: gcc.exe only works properly if the corresponding bin/ directory is
+ # contained in PATH.
+ GETH_CC: '%GETH_MINGW%\bin\gcc.exe'
+ PATH: '%GETH_MINGW%\bin;C:\Program Files (x86)\NSIS\;%PATH%'
+ build_script:
+ - 'echo %GETH_ARCH%'
+ - 'echo %GETH_CC%'
+ - '%GETH_CC% --version'
+ - go run build/ci.go install -dlgo -arch %GETH_ARCH% -cc %GETH_CC%
+ after_build:
+ # Upload builds. Note that ci.go makes this a no-op PR builds.
+ - go run build/ci.go archive -arch %GETH_ARCH% -type zip -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
+ - go run build/ci.go nsis -arch %GETH_ARCH% -signer WINDOWS_SIGNING_KEY -upload gethstore/builds
+ test_script:
+ - go run build/ci.go test -dlgo -arch %GETH_ARCH% -cc %GETH_CC% -coverage
diff --git a/build/checksums.txt b/build/checksums.txt
index e667b30ce6..48a13b53e8 100644
--- a/build/checksums.txt
+++ b/build/checksums.txt
@@ -1,33 +1,37 @@
# This file contains sha256 checksums of optional build dependencies.
-ae4f6b6e2a1677d31817984655a762074b5356da50fb58722b99104870d43503 go1.16.4.src.tar.gz
-18fe94775763db3878717393b6d41371b0b45206055e49b3838328120c977d13 go1.16.4.darwin-amd64.tar.gz
-cb6b972cc42e669f3585c648198cd5b6f6d7a0811d413ad64b50c02ba06ccc3a go1.16.4.darwin-arm64.tar.gz
-cd1b146ef6e9006f27dd99e9687773e7fef30e8c985b7d41bff33e955a3bb53a go1.16.4.linux-386.tar.gz
-7154e88f5a8047aad4b80ebace58a059e36e7e2e4eb3b383127a28c711b4ff59 go1.16.4.linux-amd64.tar.gz
-8b18eb05ddda2652d69ab1b1dd1f40dd731799f43c6a58b512ad01ae5b5bba21 go1.16.4.linux-arm64.tar.gz
-a53391a800ddec749ee90d38992babb27b95cfb864027350c737b9aa8e069494 go1.16.4.linux-armv6l.tar.gz
-e75c0b114a09eb5499874162b208931dc260de0fedaeedac8621bf263c974605 go1.16.4.windows-386.zip
-d40139b7ade8a3008e3240a6f86fe8f899a9c465c917e11dac8758af216f5eb0 go1.16.4.windows-amd64.zip
-7cf2bc8a175d6d656861165bfc554f92dc78d2abf5afe5631db3579555d97409 go1.16.4.freebsd-386.tar.gz
-ccdd2b76de1941b60734408fda0d750aaa69330d8a07430eed4c56bdb3502f6f go1.16.4.freebsd-amd64.tar.gz
-80cfac566e344096a8df8f37bbd21f89e76a6fbe601406565d71a87a665fc125 go1.16.4.linux-ppc64le.tar.gz
-d6431881b3573dc29ecc24fbeab5e5ec25d8c9273aa543769c86a1a3bbac1ddf go1.16.4.linux-s390x.tar.gz
+2255eb3e4e824dd7d5fcdc2e7f84534371c186312e546fb1086a34c17752f431 go1.17.2.src.tar.gz
+7914497a302a132a465d33f5ee044ce05568bacdb390ab805cb75a3435a23f94 go1.17.2.darwin-amd64.tar.gz
+ce8771bd3edfb5b28104084b56bbb532eeb47fbb7769c3e664c6223712c30904 go1.17.2.darwin-arm64.tar.gz
+8cea5b8d1f8e8cbb58069bfed58954c71c5b1aca2f3c857765dae83bf724d0d7 go1.17.2.freebsd-386.tar.gz
+c96e57218fb03e74d683ad63b1684d44c89d5e5b994f36102b33dce21b58499a go1.17.2.freebsd-amd64.tar.gz
+8617f2e40d51076983502894181ae639d1d8101bfbc4d7463a2b442f239f5596 go1.17.2.linux-386.tar.gz
+f242a9db6a0ad1846de7b6d94d507915d14062660616a61ef7c808a76e4f1676 go1.17.2.linux-amd64.tar.gz
+a5a43c9cdabdb9f371d56951b14290eba8ce2f9b0db48fb5fc657943984fd4fc go1.17.2.linux-arm64.tar.gz
+04d16105008230a9763005be05606f7eb1c683a3dbf0fbfed4034b23889cb7f2 go1.17.2.linux-armv6l.tar.gz
+12e2dc7e0ffeebe77083f267ef6705fec1621cdf2ed6489b3af04a13597ed68d go1.17.2.linux-ppc64le.tar.gz
+c4b2349a8d11350ca038b8c57f3cc58dc0b31284bcbed4f7fca39aeed28b4a51 go1.17.2.linux-s390x.tar.gz
+8a85257a351996fdf045fe95ed5fdd6917dd48636d562dd11dedf193005a53e0 go1.17.2.windows-386.zip
+fa6da0b829a66f5fab7e4e312fd6aa1b2d8f045c7ecee83b3d00f6fe5306759a go1.17.2.windows-amd64.zip
+00575c85dc7a129ba892685a456b27a3f3670f71c8bfde1c5ad151f771d55df7 go1.17.2.windows-arm64.zip
-7e9a47ab540aa3e8472fbf8120d28bed3b9d9cf625b955818e8bc69628d7187c golangci-lint-1.39.0-darwin-amd64.tar.gz
-574daa2c9c299b01672a6daeb1873b5f12e413cdb6dc0e30f2ff163956778064 golangci-lint-1.39.0-darwin-arm64.tar.gz
-6225f7014987324ab78e9b511f294e3f25be013728283c33918c67c8576d543e golangci-lint-1.39.0-freebsd-386.tar.gz
-6b3e76e1e5eaf0159411c8e2727f8d533989d3bb19f10e9caa6e0b9619ee267d golangci-lint-1.39.0-freebsd-amd64.tar.gz
-a301cacfff87ed9b00313d95278533c25a4527a06b040a17d969b4b7e1b8a90d golangci-lint-1.39.0-freebsd-armv7.tar.gz
-25bfd96a29c3112f508d5e4fc860dbad7afce657233c343acfa20715717d51e7 golangci-lint-1.39.0-freebsd-armv6.tar.gz
-9687e4ff15545cfc722b0e46107a94195166a505023b48a316579af25ad09505 golangci-lint-1.39.0-linux-armv7.tar.gz
-a7fa7ab2bfc99cbe5e5bcbf5684f5a997f920afbbe2f253d2feb1001d5e3c8b3 golangci-lint-1.39.0-linux-armv6.tar.gz
-c8f9634115beddb4ed9129c1f7ecd4c97c99d07aeef33e3707234097eeb51b7b golangci-lint-1.39.0-linux-mips64le.tar.gz
-d1234c213b74751f1af413302dde0e9a6d4d29aecef034af7abb07dc1b6e887f golangci-lint-1.39.0-linux-arm64.tar.gz
-df25d9267168323b163147acb823ab0215a8a3bb6898a4a9320afdfedde66817 golangci-lint-1.39.0-linux-386.tar.gz
-1767e75fba357b7651b1a796d38453558f371c60af805505ec99e166908c04b5 golangci-lint-1.39.0-linux-ppc64le.tar.gz
-25fd75bf3186b3d930ecae10185689968fd18fd8fa6f9f555d6beb04348c20f6 golangci-lint-1.39.0-linux-s390x.tar.gz
-3a73aa7468087caa62673c8adea99b4e4dff846dc72707222db85f8679b40cbf golangci-lint-1.39.0-linux-amd64.tar.gz
-578caceccf81739bda67dbfec52816709d03608c6878888ecdc0e186a094a41b golangci-lint-1.39.0-linux-mips64.tar.gz
-494b66ba0e32c8ddf6c4f6b1d05729b110900f6017eda943057e43598c17d7a8 golangci-lint-1.39.0-windows-386.zip
-52ec2e13a3cbb47147244dff8cfc35103563deb76e0459133058086fc35fb2c7 golangci-lint-1.39.0-windows-amd64.zip
+d4bd25b9814eeaa2134197dd2c7671bb791eae786d42010d9d788af20dee4bfa golangci-lint-1.42.0-darwin-amd64.tar.gz
+e56859c04a2ad5390c6a497b1acb1cc9329ecb1010260c6faae9b5a4c35b35ea golangci-lint-1.42.0-darwin-arm64.tar.gz
+14d912a3fa856830339472fc4dc341933adf15f37bdb7130bbbfcf960ecf4809 golangci-lint-1.42.0-freebsd-386.tar.gz
+337257fccc9baeb5ee1cd7e70c153e9d9f59d3afde46d631659500048afbdf80 golangci-lint-1.42.0-freebsd-amd64.tar.gz
+6debcc266b629359fdd8eef4f4abb05a621604079d27016265afb5b4593b0eff golangci-lint-1.42.0-freebsd-armv6.tar.gz
+878f0e190169db2ce9dde8cefbd99adc4fe28b90b68686bbfcfcc2085e6d693e golangci-lint-1.42.0-freebsd-armv7.tar.gz
+42c78e31faf62b225363eff1b1d2aa74f9dbcb75686c8914aa3e90d6af65cece golangci-lint-1.42.0-linux-386.tar.gz
+6937f62f8e2329e94822dc11c10b871ace5557ae1fcc4ee2f9980cd6aecbc159 golangci-lint-1.42.0-linux-amd64.tar.gz
+2cf8d23d96cd854a537b355dab2962b960b88a06b615232599f066afd233f246 golangci-lint-1.42.0-linux-arm64.tar.gz
+08b003d1ed61367473886defc957af5301066e62338e5d96a319c34dadc4c1d1 golangci-lint-1.42.0-linux-armv6.tar.gz
+c7c00ec4845e806a1f32685f5b150219e180bd6d6a9d584be8d27f0c41d7a1bf golangci-lint-1.42.0-linux-armv7.tar.gz
+3650fcf29eb3d8ee326d77791a896b15259eb2d5bf77437dc72e7efe5af6bd40 golangci-lint-1.42.0-linux-mips64.tar.gz
+f51ae003fdbca4fef78ba73e2eb736a939c8eaa178cd452234213b489da5a420 golangci-lint-1.42.0-linux-mips64le.tar.gz
+1b0bb7b8b22cc4ea7da44fd5ad5faaf6111d0677e01cc6f961b62a96537de2c6 golangci-lint-1.42.0-linux-ppc64le.tar.gz
+8cb56927eb75e572450efbe0ff0f9cf3f56dc9faa81d9e8d30d6559fc1d06e6d golangci-lint-1.42.0-linux-riscv64.tar.gz
+5ac41cd31825a176b21505a371a7b307cd9cdf17df0f35bbb3bf1466f9356ccc golangci-lint-1.42.0-linux-s390x.tar.gz
+e1cebd2af621ac4b64c20937df92c3819264f2174c92f51e196db1e64ae097e0 golangci-lint-1.42.0-windows-386.zip
+7e70fcde8e87a17cae0455df07d257ebc86669f3968d568e12727fa24bbe9883 golangci-lint-1.42.0-windows-amd64.zip
+59da7ce1bda432616bfc28ae663e52c3675adee8d9bf5959fafd657c159576ab golangci-lint-1.42.0-windows-armv6.zip
+65f62dda937bfcede0326ac77abe947ce1548931e6e13298ca036cb31f224db5 golangci-lint-1.42.0-windows-armv7.zip
diff --git a/build/ci.go b/build/ci.go
index d7d2ce72e0..1e2547fbb0 100644
--- a/build/ci.go
+++ b/build/ci.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 none
// +build none
/*
@@ -32,7 +33,6 @@ Available commands are:
nsis -- creates a Windows NSIS installer
aar [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an Android archive
xcode [ -local ] [ -sign key-id ] [-deploy repo] [ -upload dest ] -- creates an iOS XCode framework
- xgo [ -alltools ] [ options ] -- cross builds according to options
purge [ -store blobstore ] [ -days threshold ] -- purges old archives from the blobstore
For all commands, -n prevents execution of external programs (dry run mode).
@@ -129,19 +129,13 @@ var (
// Distros for which packages are created.
// Note: vivid is unsupported because there is no golang-1.6 package for it.
- // Note: wily is unsupported because it was officially deprecated on Launchpad.
- // Note: yakkety is unsupported because it was officially deprecated on Launchpad.
- // Note: zesty is unsupported because it was officially deprecated on Launchpad.
- // Note: artful is unsupported because it was officially deprecated on Launchpad.
- // Note: cosmic is unsupported because it was officially deprecated on Launchpad.
- // Note: disco is unsupported because it was officially deprecated on Launchpad.
- // Note: eoan is unsupported because it was officially deprecated on Launchpad.
+ // Note: the following Ubuntu releases have been officially deprecated on Launchpad:
+ // wily, yakkety, zesty, artful, cosmic, disco, eoan, groovy
debDistroGoBoots = map[string]string{
"trusty": "golang-1.11",
"xenial": "golang-go",
"bionic": "golang-go",
"focal": "golang-go",
- "groovy": "golang-go",
"hirsute": "golang-go",
}
@@ -153,7 +147,7 @@ var (
// This is the version of go that will be downloaded by
//
// go run ci.go install -dlgo
- dlgoVersion = "1.16.4"
+ dlgoVersion = "1.17.2"
)
var GOBIN, _ = filepath.Abs(filepath.Join("build", "bin"))
@@ -193,8 +187,6 @@ func main() {
doAndroidArchive(os.Args[2:])
case "xcode":
doXCodeFramework(os.Args[2:])
- case "xgo":
- doXgo(os.Args[2:])
case "purge":
doPurge(os.Args[2:])
default:
@@ -265,6 +257,11 @@ func buildFlags(env build.Environment) (flags []string) {
if runtime.GOOS == "darwin" {
ld = append(ld, "-s")
}
+ // Enforce the stacksize to 8M, which is the case on most platforms apart from
+ // alpine Linux.
+ if runtime.GOOS == "linux" {
+ ld = append(ld, "-extldflags", "-Wl,-z,stack-size=0x800000")
+ }
if len(ld) > 0 {
flags = append(flags, "-ldflags", strings.Join(ld, " "))
}
@@ -282,6 +279,7 @@ func doTest(cmdline []string) {
cc = flag.String("cc", "", "Sets C compiler binary")
coverage = flag.Bool("coverage", false, "Whether to record code coverage")
verbose = flag.Bool("v", false, "Whether to log verbosely")
+ race = flag.Bool("race", false, "Execute the race detector")
)
flag.CommandLine.Parse(cmdline)
@@ -302,6 +300,9 @@ func doTest(cmdline []string) {
if *verbose {
gotest.Args = append(gotest.Args, "-v")
}
+ if *race {
+ gotest.Args = append(gotest.Args, "-race")
+ }
packages := []string{"./..."}
if len(flag.CommandLine.Args()) > 0 {
@@ -330,7 +331,7 @@ func doLint(cmdline []string) {
// downloadLinter downloads and unpacks golangci-lint.
func downloadLinter(cachedir string) string {
- const version = "1.39.0"
+ const version = "1.42.0"
csdb := build.MustLoadChecksums("build/checksums.txt")
base := fmt.Sprintf("golangci-lint-%s-%s-%s", version, runtime.GOOS, runtime.GOARCH)
@@ -1205,48 +1206,6 @@ func newPodMetadata(env build.Environment, archive string) podMetadata {
}
}
-// Cross compilation
-
-func doXgo(cmdline []string) {
- var (
- alltools = flag.Bool("alltools", false, `Flag whether we're building all known tools, or only on in particular`)
- )
- flag.CommandLine.Parse(cmdline)
- env := build.Env()
- var tc build.GoToolchain
-
- // Make sure xgo is available for cross compilation
- build.MustRun(tc.Install(GOBIN, "github.com/karalabe/xgo@latest"))
-
- // If all tools building is requested, build everything the builder wants
- args := append(buildFlags(env), flag.Args()...)
-
- if *alltools {
- args = append(args, []string{"--dest", GOBIN}...)
- for _, res := range allToolsArchiveFiles {
- if strings.HasPrefix(res, GOBIN) {
- // Binary tool found, cross build it explicitly
- args = append(args, "./"+filepath.Join("cmd", filepath.Base(res)))
- build.MustRun(xgoTool(args))
- args = args[:len(args)-1]
- }
- }
- return
- }
-
- // Otherwise execute the explicit cross compilation
- path := args[len(args)-1]
- args = append(args[:len(args)-1], []string{"--dest", GOBIN, path}...)
- build.MustRun(xgoTool(args))
-}
-
-func xgoTool(args []string) *exec.Cmd {
- cmd := exec.Command(filepath.Join(GOBIN, "xgo"), args...)
- cmd.Env = os.Environ()
- cmd.Env = append(cmd.Env, []string{"GOBIN=" + GOBIN}...)
- return cmd
-}
-
// Binary distribution cleanups
func doPurge(cmdline []string) {
diff --git a/cmd/devp2p/internal/ethtest/chain.go b/cmd/devp2p/internal/ethtest/chain.go
index 34a20c515b..7dcb412b53 100644
--- a/cmd/devp2p/internal/ethtest/chain.go
+++ b/cmd/devp2p/internal/ethtest/chain.go
@@ -39,16 +39,6 @@ type Chain struct {
chainConfig *params.ChainConfig
}
-func (c *Chain) WriteTo(writer io.Writer) error {
- for _, block := range c.blocks {
- if err := rlp.Encode(writer, block); err != nil {
- return err
- }
- }
-
- return nil
-}
-
// Len returns the length of the chain.
func (c *Chain) Len() int {
return len(c.blocks)
diff --git a/cmd/devp2p/internal/ethtest/helpers.go b/cmd/devp2p/internal/ethtest/helpers.go
index 6f7365483a..88d8e143c1 100644
--- a/cmd/devp2p/internal/ethtest/helpers.go
+++ b/cmd/devp2p/internal/ethtest/helpers.go
@@ -242,9 +242,17 @@ func (s *Suite) createSendAndRecvConns(isEth66 bool) (*Conn, *Conn, error) {
return sendConn, recvConn, nil
}
+func (c *Conn) readAndServe(chain *Chain, timeout time.Duration) Message {
+ if c.negotiatedProtoVersion == 66 {
+ _, msg := c.readAndServe66(chain, timeout)
+ return msg
+ }
+ return c.readAndServe65(chain, timeout)
+}
+
// readAndServe serves GetBlockHeaders requests while waiting
// on another message from the node.
-func (c *Conn) readAndServe(chain *Chain, timeout time.Duration) Message {
+func (c *Conn) readAndServe65(chain *Chain, timeout time.Duration) Message {
start := time.Now()
for time.Since(start) < timeout {
c.SetReadDeadline(time.Now().Add(5 * time.Second))
@@ -279,8 +287,8 @@ func (c *Conn) readAndServe66(chain *Chain, timeout time.Duration) (uint64, Mess
switch msg := msg.(type) {
case *Ping:
c.Write(&Pong{})
- case *GetBlockHeaders:
- headers, err := chain.GetHeaders(*msg)
+ case GetBlockHeaders:
+ headers, err := chain.GetHeaders(msg)
if err != nil {
return 0, errorf("could not get headers for inbound header request: %v", err)
}
diff --git a/cmd/devp2p/internal/ethtest/suite_test.go b/cmd/devp2p/internal/ethtest/suite_test.go
index 6e3217151a..6d14404e66 100644
--- a/cmd/devp2p/internal/ethtest/suite_test.go
+++ b/cmd/devp2p/internal/ethtest/suite_test.go
@@ -45,7 +45,7 @@ func TestEthSuite(t *testing.T) {
if err != nil {
t.Fatalf("could not create new test suite: %v", err)
}
- for _, test := range suite.AllEthTests() {
+ for _, test := range suite.Eth66Tests() {
t.Run(test.Name, func(t *testing.T) {
result := utesting.RunTAP([]utesting.Test{{Name: test.Name, Fn: test.Fn}}, os.Stdout)
if result[0].Failed {
diff --git a/cmd/evm/README.md b/cmd/evm/README.md
index d5257069f2..1a029ab709 100644
--- a/cmd/evm/README.md
+++ b/cmd/evm/README.md
@@ -208,7 +208,7 @@ Example:
]
}
```
-When applying this, using a reward of `0x08`
+When applying this, using a reward of `0x80`
Output:
```json
{
diff --git a/cmd/evm/disasm.go b/cmd/evm/disasm.go
index 68a09cbf50..f9719497fe 100644
--- a/cmd/evm/disasm.go
+++ b/cmd/evm/disasm.go
@@ -46,7 +46,7 @@ func disasmCmd(ctx *cli.Context) error {
case ctx.GlobalIsSet(InputFlag.Name):
in = ctx.GlobalString(InputFlag.Name)
default:
- return errors.New("Missing filename or --input value")
+ return errors.New("missing filename or --input value")
}
code := strings.TrimSpace(in)
diff --git a/cmd/evm/internal/t8ntool/execution.go b/cmd/evm/internal/t8ntool/execution.go
index 1ab2f001e2..fae65767be 100644
--- a/cmd/evm/internal/t8ntool/execution.go
+++ b/cmd/evm/internal/t8ntool/execution.go
@@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/consensus/misc"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
@@ -46,13 +47,14 @@ type Prestate struct {
// ExecutionResult contains the execution status after running a state test, any
// error that might have occurred and a dump of the final state if requested.
type ExecutionResult struct {
- StateRoot common.Hash `json:"stateRoot"`
- TxRoot common.Hash `json:"txRoot"`
- ReceiptRoot common.Hash `json:"receiptRoot"`
- LogsHash common.Hash `json:"logsHash"`
- Bloom types.Bloom `json:"logsBloom" gencodec:"required"`
- Receipts types.Receipts `json:"receipts"`
- Rejected []*rejectedTx `json:"rejected,omitempty"`
+ StateRoot common.Hash `json:"stateRoot"`
+ TxRoot common.Hash `json:"txRoot"`
+ ReceiptRoot common.Hash `json:"receiptRoot"`
+ LogsHash common.Hash `json:"logsHash"`
+ Bloom types.Bloom `json:"logsBloom" gencodec:"required"`
+ Receipts types.Receipts `json:"receipts"`
+ Rejected []*rejectedTx `json:"rejected,omitempty"`
+ Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
}
type ommer struct {
@@ -62,23 +64,28 @@ type ommer struct {
//go:generate gencodec -type stEnv -field-override stEnvMarshaling -out gen_stenv.go
type stEnv struct {
- Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
- Difficulty *big.Int `json:"currentDifficulty" gencodec:"required"`
- GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
- Number uint64 `json:"currentNumber" gencodec:"required"`
- Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
- BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
- Ommers []ommer `json:"ommers,omitempty"`
- BaseFee *big.Int `json:"currentBaseFee,omitempty"`
+ Coinbase common.Address `json:"currentCoinbase" gencodec:"required"`
+ Difficulty *big.Int `json:"currentDifficulty"`
+ ParentDifficulty *big.Int `json:"parentDifficulty"`
+ GasLimit uint64 `json:"currentGasLimit" gencodec:"required"`
+ Number uint64 `json:"currentNumber" gencodec:"required"`
+ Timestamp uint64 `json:"currentTimestamp" gencodec:"required"`
+ ParentTimestamp uint64 `json:"parentTimestamp,omitempty"`
+ BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
+ Ommers []ommer `json:"ommers,omitempty"`
+ BaseFee *big.Int `json:"currentBaseFee,omitempty"`
+ ParentUncleHash common.Hash `json:"parentUncleHash"`
}
type stEnvMarshaling struct {
- Coinbase common.UnprefixedAddress
- Difficulty *math.HexOrDecimal256
- GasLimit math.HexOrDecimal64
- Number math.HexOrDecimal64
- Timestamp math.HexOrDecimal64
- BaseFee *math.HexOrDecimal256
+ Coinbase common.UnprefixedAddress
+ Difficulty *math.HexOrDecimal256
+ ParentDifficulty *math.HexOrDecimal256
+ GasLimit math.HexOrDecimal64
+ Number math.HexOrDecimal64
+ Timestamp math.HexOrDecimal64
+ ParentTimestamp math.HexOrDecimal64
+ BaseFee *math.HexOrDecimal256
}
type rejectedTx struct {
@@ -247,6 +254,7 @@ func (pre *Prestate) Apply(vmConfig vm.Config, chainConfig *params.ChainConfig,
LogsHash: rlpHash(statedb.Logs()),
Receipts: receipts,
Rejected: rejectedTxs,
+ Difficulty: (*math.HexOrDecimal256)(vmContext.Difficulty),
}
return statedb, execRs, nil
}
@@ -274,3 +282,23 @@ func rlpHash(x interface{}) (h common.Hash) {
hw.Sum(h[:0])
return h
}
+
+// calcDifficulty is based on ethash.CalcDifficulty. This method is used in case
+// the caller does not provide an explicit difficulty, but instead provides only
+// parent timestamp + difficulty.
+// Note: this method only works for ethash engine.
+func calcDifficulty(config *params.ChainConfig, number, currentTime, parentTime uint64,
+ parentDifficulty *big.Int, parentUncleHash common.Hash) *big.Int {
+ uncleHash := parentUncleHash
+ if uncleHash == (common.Hash{}) {
+ uncleHash = types.EmptyUncleHash
+ }
+ parent := &types.Header{
+ ParentHash: common.Hash{},
+ UncleHash: uncleHash,
+ Difficulty: parentDifficulty,
+ Number: new(big.Int).SetUint64(number - 1),
+ Time: parentTime,
+ }
+ return ethash.CalcDifficulty(config, currentTime, parent)
+}
diff --git a/cmd/evm/internal/t8ntool/flags.go b/cmd/evm/internal/t8ntool/flags.go
index 626f974a0b..05b6ed1647 100644
--- a/cmd/evm/internal/t8ntool/flags.go
+++ b/cmd/evm/internal/t8ntool/flags.go
@@ -30,7 +30,7 @@ var (
Name: "trace",
Usage: "Output full trace logs to files .jsonl",
}
- TraceDisableMemoryFlag = cli.BoolFlag{
+ TraceDisableMemoryFlag = cli.BoolTFlag{
Name: "trace.nomemory",
Usage: "Disable full memory dump in traces",
}
@@ -38,7 +38,7 @@ var (
Name: "trace.nostack",
Usage: "Disable stack output in traces",
}
- TraceDisableReturnDataFlag = cli.BoolFlag{
+ TraceDisableReturnDataFlag = cli.BoolTFlag{
Name: "trace.noreturndata",
Usage: "Disable return data output in traces",
}
diff --git a/cmd/evm/internal/t8ntool/gen_stenv.go b/cmd/evm/internal/t8ntool/gen_stenv.go
index c7f079c02f..1bb3c6a46b 100644
--- a/cmd/evm/internal/t8ntool/gen_stenv.go
+++ b/cmd/evm/internal/t8ntool/gen_stenv.go
@@ -16,38 +16,47 @@ var _ = (*stEnvMarshaling)(nil)
// MarshalJSON marshals as JSON.
func (s stEnv) MarshalJSON() ([]byte, error) {
type stEnv struct {
- Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
- Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
- GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
- Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
- Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
- BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
- Ommers []ommer `json:"ommers,omitempty"`
- BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
+ Coinbase common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
+ Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"`
+ ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"`
+ GasLimit math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
+ Number math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
+ Timestamp math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
+ ParentTimestamp math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
+ BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
+ Ommers []ommer `json:"ommers,omitempty"`
+ BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
+ ParentUncleHash common.Hash `json:"parentUncleHash"`
}
var enc stEnv
enc.Coinbase = common.UnprefixedAddress(s.Coinbase)
enc.Difficulty = (*math.HexOrDecimal256)(s.Difficulty)
+ enc.ParentDifficulty = (*math.HexOrDecimal256)(s.ParentDifficulty)
enc.GasLimit = math.HexOrDecimal64(s.GasLimit)
enc.Number = math.HexOrDecimal64(s.Number)
enc.Timestamp = math.HexOrDecimal64(s.Timestamp)
+ enc.ParentTimestamp = math.HexOrDecimal64(s.ParentTimestamp)
enc.BlockHashes = s.BlockHashes
enc.Ommers = s.Ommers
enc.BaseFee = (*math.HexOrDecimal256)(s.BaseFee)
+ enc.ParentUncleHash = s.ParentUncleHash
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"`
- Difficulty *math.HexOrDecimal256 `json:"currentDifficulty" gencodec:"required"`
- GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
- Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
- Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
- BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
- Ommers []ommer `json:"ommers,omitempty"`
- BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
+ Coinbase *common.UnprefixedAddress `json:"currentCoinbase" gencodec:"required"`
+ Difficulty *math.HexOrDecimal256 `json:"currentDifficulty"`
+ ParentDifficulty *math.HexOrDecimal256 `json:"parentDifficulty"`
+ GasLimit *math.HexOrDecimal64 `json:"currentGasLimit" gencodec:"required"`
+ Number *math.HexOrDecimal64 `json:"currentNumber" gencodec:"required"`
+ Timestamp *math.HexOrDecimal64 `json:"currentTimestamp" gencodec:"required"`
+ ParentTimestamp *math.HexOrDecimal64 `json:"parentTimestamp,omitempty"`
+ BlockHashes map[math.HexOrDecimal64]common.Hash `json:"blockHashes,omitempty"`
+ Ommers []ommer `json:"ommers,omitempty"`
+ BaseFee *math.HexOrDecimal256 `json:"currentBaseFee,omitempty"`
+ ParentUncleHash *common.Hash `json:"parentUncleHash"`
}
var dec stEnv
if err := json.Unmarshal(input, &dec); err != nil {
@@ -57,10 +66,12 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
return errors.New("missing required field 'currentCoinbase' for stEnv")
}
s.Coinbase = common.Address(*dec.Coinbase)
- if dec.Difficulty == nil {
- return errors.New("missing required field 'currentDifficulty' for stEnv")
+ if dec.Difficulty != nil {
+ s.Difficulty = (*big.Int)(dec.Difficulty)
+ }
+ if dec.ParentDifficulty != nil {
+ s.ParentDifficulty = (*big.Int)(dec.ParentDifficulty)
}
- s.Difficulty = (*big.Int)(dec.Difficulty)
if dec.GasLimit == nil {
return errors.New("missing required field 'currentGasLimit' for stEnv")
}
@@ -73,6 +84,9 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
return errors.New("missing required field 'currentTimestamp' for stEnv")
}
s.Timestamp = uint64(*dec.Timestamp)
+ if dec.ParentTimestamp != nil {
+ s.ParentTimestamp = uint64(*dec.ParentTimestamp)
+ }
if dec.BlockHashes != nil {
s.BlockHashes = dec.BlockHashes
}
@@ -82,5 +96,8 @@ func (s *stEnv) UnmarshalJSON(input []byte) error {
if dec.BaseFee != nil {
s.BaseFee = (*big.Int)(dec.BaseFee)
}
+ if dec.ParentUncleHash != nil {
+ s.ParentUncleHash = *dec.ParentUncleHash
+ }
return nil
}
diff --git a/cmd/evm/internal/t8ntool/transaction.go b/cmd/evm/internal/t8ntool/transaction.go
new file mode 100644
index 0000000000..bc9bc42edc
--- /dev/null
+++ b/cmd/evm/internal/t8ntool/transaction.go
@@ -0,0 +1,177 @@
+// Copyright 2021 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 t8ntool
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "math/big"
+ "os"
+ "strings"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/ethereum/go-ethereum/tests"
+ "gopkg.in/urfave/cli.v1"
+)
+
+type result struct {
+ Error error
+ Address common.Address
+ Hash common.Hash
+ IntrinsicGas uint64
+}
+
+// MarshalJSON marshals as JSON with a hash.
+func (r *result) MarshalJSON() ([]byte, error) {
+ type xx struct {
+ Error string `json:"error,omitempty"`
+ Address *common.Address `json:"address,omitempty"`
+ Hash *common.Hash `json:"hash,omitempty"`
+ IntrinsicGas uint64 `json:"intrinsicGas,omitempty"`
+ }
+ var out xx
+ if r.Error != nil {
+ out.Error = r.Error.Error()
+ }
+ if r.Address != (common.Address{}) {
+ out.Address = &r.Address
+ }
+ if r.Hash != (common.Hash{}) {
+ out.Hash = &r.Hash
+ }
+ out.IntrinsicGas = r.IntrinsicGas
+ return json.Marshal(out)
+}
+
+func Transaction(ctx *cli.Context) error {
+ // Configure the go-ethereum logger
+ glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
+ glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
+ log.Root().SetHandler(glogger)
+
+ var (
+ err error
+ )
+ // We need to load the transactions. May be either in stdin input or in files.
+ // Check if anything needs to be read from stdin
+ var (
+ txStr = ctx.String(InputTxsFlag.Name)
+ inputData = &input{}
+ chainConfig *params.ChainConfig
+ )
+ // Construct the chainconfig
+ if cConf, _, err := tests.GetChainConfig(ctx.String(ForknameFlag.Name)); err != nil {
+ return NewError(ErrorVMConfig, fmt.Errorf("failed constructing chain configuration: %v", err))
+ } else {
+ chainConfig = cConf
+ }
+ // Set the chain id
+ chainConfig.ChainID = big.NewInt(ctx.Int64(ChainIDFlag.Name))
+ var body hexutil.Bytes
+ if txStr == stdinSelector {
+ decoder := json.NewDecoder(os.Stdin)
+ if err := decoder.Decode(inputData); err != nil {
+ return NewError(ErrorJson, fmt.Errorf("failed unmarshaling stdin: %v", err))
+ }
+ // Decode the body of already signed transactions
+ body = common.FromHex(inputData.TxRlp)
+ } else {
+ // Read input from file
+ inFile, err := os.Open(txStr)
+ if err != nil {
+ return NewError(ErrorIO, fmt.Errorf("failed reading txs file: %v", err))
+ }
+ defer inFile.Close()
+ decoder := json.NewDecoder(inFile)
+ if strings.HasSuffix(txStr, ".rlp") {
+ if err := decoder.Decode(&body); err != nil {
+ return err
+ }
+ } else {
+ return NewError(ErrorIO, errors.New("only rlp supported"))
+ }
+ }
+ signer := types.MakeSigner(chainConfig, new(big.Int))
+ // We now have the transactions in 'body', which is supposed to be an
+ // rlp list of transactions
+ it, err := rlp.NewListIterator([]byte(body))
+ if err != nil {
+ return err
+ }
+ var results []result
+ for it.Next() {
+ if err := it.Err(); err != nil {
+ return NewError(ErrorIO, err)
+ }
+ var tx types.Transaction
+ err := rlp.DecodeBytes(it.Value(), &tx)
+ if err != nil {
+ results = append(results, result{Error: err})
+ continue
+ }
+ r := result{Hash: tx.Hash()}
+ if sender, err := types.Sender(signer, &tx); err != nil {
+ r.Error = err
+ results = append(results, r)
+ continue
+ } else {
+ r.Address = sender
+ }
+ // Check intrinsic gas
+ if gas, err := core.IntrinsicGas(tx.Data(), tx.AccessList(), tx.To() == nil,
+ chainConfig.IsHomestead(new(big.Int)), chainConfig.IsIstanbul(new(big.Int))); err != nil {
+ r.Error = err
+ results = append(results, r)
+ continue
+ } else {
+ r.IntrinsicGas = gas
+ if tx.Gas() < gas {
+ r.Error = fmt.Errorf("%w: have %d, want %d", core.ErrIntrinsicGas, tx.Gas(), gas)
+ results = append(results, r)
+ continue
+ }
+ }
+ // Validate <256bit fields
+ switch {
+ case tx.Value().BitLen() > 256:
+ r.Error = errors.New("value exceeds 256 bits")
+ case tx.GasPrice().BitLen() > 256:
+ r.Error = errors.New("gasPrice exceeds 256 bits")
+ case tx.GasTipCap().BitLen() > 256:
+ r.Error = errors.New("maxPriorityFeePerGas exceeds 256 bits")
+ case tx.GasFeeCap().BitLen() > 256:
+ r.Error = errors.New("maxFeePerGas exceeds 256 bits")
+ case tx.GasFeeCap().Cmp(tx.GasTipCap()) < 0:
+ r.Error = errors.New("maxFeePerGas < maxPriorityFeePerGas")
+ case new(big.Int).Mul(tx.GasPrice(), new(big.Int).SetUint64(tx.Gas())).BitLen() > 256:
+ r.Error = errors.New("gas * gasPrice exceeds 256 bits")
+ case new(big.Int).Mul(tx.GasFeeCap(), new(big.Int).SetUint64(tx.Gas())).BitLen() > 256:
+ r.Error = errors.New("gas * maxFeePerGas exceeds 256 bits")
+ }
+ results = append(results, r)
+ }
+ out, err := json.MarshalIndent(results, "", " ")
+ fmt.Println(string(out))
+ return err
+}
diff --git a/cmd/evm/internal/t8ntool/transition.go b/cmd/evm/internal/t8ntool/transition.go
index 8334aa01d4..88a9c5e622 100644
--- a/cmd/evm/internal/t8ntool/transition.go
+++ b/cmd/evm/internal/t8ntool/transition.go
@@ -65,10 +65,15 @@ func (n *NumberedError) Error() string {
return fmt.Sprintf("ERROR(%d): %v", n.errorCode, n.err.Error())
}
-func (n *NumberedError) Code() int {
+func (n *NumberedError) ExitCode() int {
return n.errorCode
}
+// compile-time conformance test
+var (
+ _ cli.ExitCoder = (*NumberedError)(nil)
+)
+
type input struct {
Alloc core.GenesisAlloc `json:"alloc,omitempty"`
Env *stEnv `json:"env,omitempty"`
@@ -76,7 +81,7 @@ type input struct {
TxRlp string `json:"txsRlp,omitempty"`
}
-func Main(ctx *cli.Context) error {
+func Transition(ctx *cli.Context) error {
// Configure the go-ethereum logger
glogger := log.NewGlogHandler(log.StreamHandler(os.Stderr, log.TerminalFormat(false)))
glogger.Verbosity(log.Lvl(ctx.Int(VerbosityFlag.Name)))
@@ -102,10 +107,10 @@ func Main(ctx *cli.Context) error {
if ctx.Bool(TraceFlag.Name) {
// Configure the EVM logger
logConfig := &vm.LogConfig{
- DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
- DisableMemory: ctx.Bool(TraceDisableMemoryFlag.Name),
- DisableReturnData: ctx.Bool(TraceDisableReturnDataFlag.Name),
- Debug: true,
+ DisableStack: ctx.Bool(TraceDisableStackFlag.Name),
+ EnableMemory: !ctx.Bool(TraceDisableMemoryFlag.Name),
+ EnableReturnData: !ctx.Bool(TraceDisableReturnDataFlag.Name),
+ Debug: true,
}
var prevFile *os.File
// This one closes the last file
@@ -252,6 +257,20 @@ func Main(ctx *cli.Context) error {
return NewError(ErrorVMConfig, errors.New("EIP-1559 config but missing 'currentBaseFee' in env section"))
}
}
+ if env := prestate.Env; env.Difficulty == nil {
+ // If difficulty was not provided by caller, we need to calculate it.
+ switch {
+ case env.ParentDifficulty == nil:
+ return NewError(ErrorVMConfig, errors.New("currentDifficulty was not provided, and cannot be calculated due to missing parentDifficulty"))
+ case env.Number == 0:
+ return NewError(ErrorVMConfig, errors.New("currentDifficulty needs to be provided for block number 0"))
+ case env.Timestamp <= env.ParentTimestamp:
+ return NewError(ErrorVMConfig, fmt.Errorf("currentDifficulty cannot be calculated -- currentTime (%d) needs to be after parent time (%d)",
+ env.Timestamp, env.ParentTimestamp))
+ }
+ prestate.Env.Difficulty = calcDifficulty(chainConfig, env.Number, env.Timestamp,
+ env.ParentTimestamp, env.ParentDifficulty, env.ParentUncleHash)
+ }
// Run the test and aggregate the result
s, result, err := prestate.Apply(vmConfig, chainConfig, txs, ctx.Int64(RewardFlag.Name), getTracer)
if err != nil {
@@ -395,20 +414,20 @@ func dispatchOutput(ctx *cli.Context, baseDir string, result *ExecutionResult, a
return err
}
if len(stdOutObject) > 0 {
- b, err := json.MarshalIndent(stdOutObject, "", " ")
+ b, err := json.MarshalIndent(stdOutObject, "", " ")
if err != nil {
return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
}
os.Stdout.Write(b)
- os.Stdout.Write([]byte("\n"))
+ os.Stdout.WriteString("\n")
}
if len(stdErrObject) > 0 {
- b, err := json.MarshalIndent(stdErrObject, "", " ")
+ b, err := json.MarshalIndent(stdErrObject, "", " ")
if err != nil {
return NewError(ErrorJson, fmt.Errorf("failed marshalling output: %v", err))
}
os.Stderr.Write(b)
- os.Stderr.Write([]byte("\n"))
+ os.Stderr.WriteString("\n")
}
return nil
}
diff --git a/cmd/evm/main.go b/cmd/evm/main.go
index b9c0d17f37..26064efc3c 100644
--- a/cmd/evm/main.go
+++ b/cmd/evm/main.go
@@ -113,7 +113,7 @@ var (
Name: "receiver",
Usage: "The transaction receiver (execution context)",
}
- DisableMemoryFlag = cli.BoolFlag{
+ DisableMemoryFlag = cli.BoolTFlag{
Name: "nomemory",
Usage: "disable memory output",
}
@@ -125,9 +125,9 @@ var (
Name: "nostorage",
Usage: "disable storage output",
}
- DisableReturnDataFlag = cli.BoolFlag{
+ DisableReturnDataFlag = cli.BoolTFlag{
Name: "noreturndata",
- Usage: "disable return data output",
+ Usage: "enable return data output",
}
)
@@ -135,7 +135,7 @@ var stateTransitionCommand = cli.Command{
Name: "transition",
Aliases: []string{"t8n"},
Usage: "executes a full state transition",
- Action: t8ntool.Main,
+ Action: t8ntool.Transition,
Flags: []cli.Flag{
t8ntool.TraceFlag,
t8ntool.TraceDisableMemoryFlag,
@@ -154,6 +154,18 @@ var stateTransitionCommand = cli.Command{
t8ntool.VerbosityFlag,
},
}
+var transactionCommand = cli.Command{
+ Name: "transaction",
+ Aliases: []string{"t9n"},
+ Usage: "performs transaction validation",
+ Action: t8ntool.Transaction,
+ Flags: []cli.Flag{
+ t8ntool.InputTxsFlag,
+ t8ntool.ChainIDFlag,
+ t8ntool.ForknameFlag,
+ t8ntool.VerbosityFlag,
+ },
+}
func init() {
app.Flags = []cli.Flag{
@@ -187,6 +199,7 @@ func init() {
runCommand,
stateTestCommand,
stateTransitionCommand,
+ transactionCommand,
}
cli.CommandHelpTemplate = flags.OriginCommandHelpTemplate
}
@@ -195,7 +208,7 @@ func main() {
if err := app.Run(os.Args); err != nil {
code := 1
if ec, ok := err.(*t8ntool.NumberedError); ok {
- code = ec.Code()
+ code = ec.ExitCode()
}
fmt.Fprintln(os.Stderr, err)
os.Exit(code)
diff --git a/cmd/evm/runner.go b/cmd/evm/runner.go
index e409d26925..cedbd22815 100644
--- a/cmd/evm/runner.go
+++ b/cmd/evm/runner.go
@@ -108,11 +108,11 @@ func runCmd(ctx *cli.Context) error {
glogger.Verbosity(log.Lvl(ctx.GlobalInt(VerbosityFlag.Name)))
log.Root().SetHandler(glogger)
logconfig := &vm.LogConfig{
- DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
- DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
- DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name),
- DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name),
- Debug: ctx.GlobalBool(DebugFlag.Name),
+ EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name),
+ DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
+ DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name),
+ EnableReturnData: !ctx.GlobalBool(DisableReturnDataFlag.Name),
+ Debug: ctx.GlobalBool(DebugFlag.Name),
}
var (
diff --git a/cmd/evm/staterunner.go b/cmd/evm/staterunner.go
index d8bc4eae80..ab27046091 100644
--- a/cmd/evm/staterunner.go
+++ b/cmd/evm/staterunner.go
@@ -59,10 +59,10 @@ func stateTestCmd(ctx *cli.Context) error {
// Configure the EVM logger
config := &vm.LogConfig{
- DisableMemory: ctx.GlobalBool(DisableMemoryFlag.Name),
- DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
- DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name),
- DisableReturnData: ctx.GlobalBool(DisableReturnDataFlag.Name),
+ EnableMemory: !ctx.GlobalBool(DisableMemoryFlag.Name),
+ DisableStack: ctx.GlobalBool(DisableStackFlag.Name),
+ DisableStorage: ctx.GlobalBool(DisableStorageFlag.Name),
+ EnableReturnData: !ctx.GlobalBool(DisableReturnDataFlag.Name),
}
var (
tracer vm.Tracer
diff --git a/cmd/evm/t8n_test.go b/cmd/evm/t8n_test.go
new file mode 100644
index 0000000000..b4b816f57a
--- /dev/null
+++ b/cmd/evm/t8n_test.go
@@ -0,0 +1,349 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "os"
+ "reflect"
+ "strings"
+ "testing"
+
+ "github.com/docker/docker/pkg/reexec"
+ "github.com/ethereum/go-ethereum/cmd/evm/internal/t8ntool"
+ "github.com/ethereum/go-ethereum/internal/cmdtest"
+)
+
+func TestMain(m *testing.M) {
+ // Run the app if we've been exec'd as "ethkey-test" in runEthkey.
+ reexec.Register("evm-test", func() {
+ if err := app.Run(os.Args); err != nil {
+ fmt.Fprintln(os.Stderr, err)
+ os.Exit(1)
+ }
+ os.Exit(0)
+ })
+ // check if we have been reexec'd
+ if reexec.Init() {
+ return
+ }
+ os.Exit(m.Run())
+}
+
+type testT8n struct {
+ *cmdtest.TestCmd
+}
+
+type t8nInput struct {
+ inAlloc string
+ inTxs string
+ inEnv string
+ stFork string
+ stReward string
+}
+
+func (args *t8nInput) get(base string) []string {
+ var out []string
+ if opt := args.inAlloc; opt != "" {
+ out = append(out, "--input.alloc")
+ out = append(out, fmt.Sprintf("%v/%v", base, opt))
+ }
+ if opt := args.inTxs; opt != "" {
+ out = append(out, "--input.txs")
+ out = append(out, fmt.Sprintf("%v/%v", base, opt))
+ }
+ if opt := args.inEnv; opt != "" {
+ out = append(out, "--input.env")
+ out = append(out, fmt.Sprintf("%v/%v", base, opt))
+ }
+ if opt := args.stFork; opt != "" {
+ out = append(out, "--state.fork", opt)
+ }
+ if opt := args.stReward; opt != "" {
+ out = append(out, "--state.reward", opt)
+ }
+ return out
+}
+
+type t8nOutput struct {
+ alloc bool
+ result bool
+ body bool
+}
+
+func (args *t8nOutput) get() (out []string) {
+ if args.body {
+ out = append(out, "--output.body", "stdout")
+ } else {
+ out = append(out, "--output.body", "") // empty means ignore
+ }
+ if args.result {
+ out = append(out, "--output.result", "stdout")
+ } else {
+ out = append(out, "--output.result", "")
+ }
+ if args.alloc {
+ out = append(out, "--output.alloc", "stdout")
+ } else {
+ out = append(out, "--output.alloc", "")
+ }
+ return out
+}
+
+func TestT8n(t *testing.T) {
+ tt := new(testT8n)
+ tt.TestCmd = cmdtest.NewTestCmd(t, tt)
+ for i, tc := range []struct {
+ base string
+ input t8nInput
+ output t8nOutput
+ expExitCode int
+ expOut string
+ }{
+ { // Test exit (3) on bad config
+ base: "./testdata/1",
+ input: t8nInput{
+ "alloc.json", "txs.json", "env.json", "Frontier+1346", "",
+ },
+ output: t8nOutput{alloc: true, result: true},
+ expExitCode: 3,
+ },
+ {
+ base: "./testdata/1",
+ input: t8nInput{
+ "alloc.json", "txs.json", "env.json", "Byzantium", "",
+ },
+ output: t8nOutput{alloc: true, result: true},
+ expOut: "exp.json",
+ },
+ { // blockhash test
+ base: "./testdata/3",
+ input: t8nInput{
+ "alloc.json", "txs.json", "env.json", "Berlin", "",
+ },
+ output: t8nOutput{alloc: true, result: true},
+ expOut: "exp.json",
+ },
+ { // missing blockhash test
+ base: "./testdata/4",
+ input: t8nInput{
+ "alloc.json", "txs.json", "env.json", "Berlin", "",
+ },
+ output: t8nOutput{alloc: true, result: true},
+ expExitCode: 4,
+ },
+ { // Ommer test
+ base: "./testdata/5",
+ input: t8nInput{
+ "alloc.json", "txs.json", "env.json", "Byzantium", "0x80",
+ },
+ output: t8nOutput{alloc: true, result: true},
+ expOut: "exp.json",
+ },
+ { // Sign json transactions
+ base: "./testdata/13",
+ input: t8nInput{
+ "alloc.json", "txs.json", "env.json", "London", "",
+ },
+ output: t8nOutput{body: true},
+ expOut: "exp.json",
+ },
+ { // Already signed transactions
+ base: "./testdata/13",
+ input: t8nInput{
+ "alloc.json", "signed_txs.rlp", "env.json", "London", "",
+ },
+ output: t8nOutput{result: true},
+ expOut: "exp2.json",
+ },
+ { // Difficulty calculation - no uncles
+ base: "./testdata/14",
+ input: t8nInput{
+ "alloc.json", "txs.json", "env.json", "London", "",
+ },
+ output: t8nOutput{result: true},
+ expOut: "exp.json",
+ },
+ { // Difficulty calculation - with uncles
+ base: "./testdata/14",
+ input: t8nInput{
+ "alloc.json", "txs.json", "env.uncles.json", "London", "",
+ },
+ output: t8nOutput{result: true},
+ expOut: "exp2.json",
+ },
+ { // Difficulty calculation - with uncles + Berlin
+ base: "./testdata/14",
+ input: t8nInput{
+ "alloc.json", "txs.json", "env.uncles.json", "Berlin", "",
+ },
+ output: t8nOutput{result: true},
+ expOut: "exp_berlin.json",
+ },
+ { // Difficulty calculation on arrow glacier
+ base: "./testdata/19",
+ input: t8nInput{
+ "alloc.json", "txs.json", "env.json", "London", "",
+ },
+ output: t8nOutput{result: true},
+ expOut: "exp_london.json",
+ },
+ { // Difficulty calculation on arrow glacier
+ base: "./testdata/19",
+ input: t8nInput{
+ "alloc.json", "txs.json", "env.json", "ArrowGlacier", "",
+ },
+ output: t8nOutput{result: true},
+ expOut: "exp_arrowglacier.json",
+ },
+ } {
+
+ args := []string{"t8n"}
+ args = append(args, tc.output.get()...)
+ args = append(args, tc.input.get(tc.base)...)
+ var qArgs []string // quoted args for debugging purposes
+ for _, arg := range args {
+ if len(arg) == 0 {
+ qArgs = append(qArgs, `""`)
+ } else {
+ qArgs = append(qArgs, arg)
+ }
+ }
+ tt.Logf("args: %v\n", strings.Join(qArgs, " "))
+ tt.Run("evm-test", args...)
+ // Compare the expected output, if provided
+ if tc.expOut != "" {
+ want, err := os.ReadFile(fmt.Sprintf("%v/%v", tc.base, tc.expOut))
+ if err != nil {
+ t.Fatalf("test %d: could not read expected output: %v", i, err)
+ }
+ have := tt.Output()
+ ok, err := cmpJson(have, want)
+ switch {
+ case err != nil:
+ t.Fatalf("test %d, json parsing failed: %v", i, err)
+ case !ok:
+ t.Fatalf("test %d: output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want))
+ }
+ }
+ tt.WaitExit()
+ if have, want := tt.ExitStatus(), tc.expExitCode; have != want {
+ t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want)
+ }
+ }
+}
+
+type t9nInput struct {
+ inTxs string
+ stFork string
+}
+
+func (args *t9nInput) get(base string) []string {
+ var out []string
+ if opt := args.inTxs; opt != "" {
+ out = append(out, "--input.txs")
+ out = append(out, fmt.Sprintf("%v/%v", base, opt))
+ }
+ if opt := args.stFork; opt != "" {
+ out = append(out, "--state.fork", opt)
+ }
+ return out
+}
+
+func TestT9n(t *testing.T) {
+ tt := new(testT8n)
+ tt.TestCmd = cmdtest.NewTestCmd(t, tt)
+ for i, tc := range []struct {
+ base string
+ input t9nInput
+ expExitCode int
+ expOut string
+ }{
+ { // London txs on homestead
+ base: "./testdata/15",
+ input: t9nInput{
+ inTxs: "signed_txs.rlp",
+ stFork: "Homestead",
+ },
+ expOut: "exp.json",
+ },
+ { // London txs on London
+ base: "./testdata/15",
+ input: t9nInput{
+ inTxs: "signed_txs.rlp",
+ stFork: "London",
+ },
+ expOut: "exp2.json",
+ },
+ { // An RLP list (a blockheader really)
+ base: "./testdata/15",
+ input: t9nInput{
+ inTxs: "blockheader.rlp",
+ stFork: "London",
+ },
+ expOut: "exp3.json",
+ },
+ { // Transactions with too low gas
+ base: "./testdata/16",
+ input: t9nInput{
+ inTxs: "signed_txs.rlp",
+ stFork: "London",
+ },
+ expOut: "exp.json",
+ },
+ { // Transactions with value exceeding 256 bits
+ base: "./testdata/17",
+ input: t9nInput{
+ inTxs: "signed_txs.rlp",
+ stFork: "London",
+ },
+ expOut: "exp.json",
+ },
+ { // Invalid RLP
+ base: "./testdata/18",
+ input: t9nInput{
+ inTxs: "invalid.rlp",
+ stFork: "London",
+ },
+ expExitCode: t8ntool.ErrorIO,
+ },
+ } {
+
+ args := []string{"t9n"}
+ args = append(args, tc.input.get(tc.base)...)
+
+ tt.Run("evm-test", args...)
+ tt.Logf("args:\n go run . %v\n", strings.Join(args, " "))
+ // Compare the expected output, if provided
+ if tc.expOut != "" {
+ want, err := os.ReadFile(fmt.Sprintf("%v/%v", tc.base, tc.expOut))
+ if err != nil {
+ t.Fatalf("test %d: could not read expected output: %v", i, err)
+ }
+ have := tt.Output()
+ ok, err := cmpJson(have, want)
+ switch {
+ case err != nil:
+ t.Logf(string(have))
+ t.Fatalf("test %d, json parsing failed: %v", i, err)
+ case !ok:
+ t.Fatalf("test %d: output wrong, have \n%v\nwant\n%v\n", i, string(have), string(want))
+ }
+ }
+ tt.WaitExit()
+ if have, want := tt.ExitStatus(), tc.expExitCode; have != want {
+ t.Fatalf("test %d: wrong exit code, have %d, want %d", i, have, want)
+ }
+ }
+}
+
+// cmpJson compares the JSON in two byte slices.
+func cmpJson(a, b []byte) (bool, error) {
+ var j, j2 interface{}
+ if err := json.Unmarshal(a, &j); err != nil {
+ return false, err
+ }
+ if err := json.Unmarshal(b, &j2); err != nil {
+ return false, err
+ }
+ return reflect.DeepEqual(j2, j), nil
+}
diff --git a/cmd/evm/testdata/1/exp.json b/cmd/evm/testdata/1/exp.json
new file mode 100644
index 0000000000..17d2f8267c
--- /dev/null
+++ b/cmd/evm/testdata/1/exp.json
@@ -0,0 +1,43 @@
+{
+ "alloc": {
+ "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192": {
+ "balance": "0xfeed1a9d",
+ "nonce": "0x1"
+ },
+ "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
+ "balance": "0x5ffd4878be161d74",
+ "nonce": "0xac"
+ },
+ "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
+ "balance": "0xa410"
+ }
+ },
+ "result": {
+ "stateRoot": "0x84208a19bc2b46ada7445180c1db162be5b39b9abc8c0a54b05d32943eae4e13",
+ "txRoot": "0xc4761fd7b87ff2364c7c60b6c5c8d02e522e815328aaea3f20e3b7b7ef52c42d",
+ "receiptRoot": "0x056b23fbba480696b65fe5a59b8f2148a1299103c4f57df839233af2cf4ca2d2",
+ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "receipts": [
+ {
+ "root": "0x",
+ "status": "0x1",
+ "cumulativeGasUsed": "0x5208",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "logs": null,
+ "transactionHash": "0x0557bacce3375c98d806609b8d5043072f0b6a8bae45ae5a67a00d3a1a18d673",
+ "contractAddress": "0x0000000000000000000000000000000000000000",
+ "gasUsed": "0x5208",
+ "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "transactionIndex": "0x0"
+ }
+ ],
+ "rejected": [
+ {
+ "index": 1,
+ "error": "nonce too low: address 0x8A8eAFb1cf62BfBeb1741769DAE1a9dd47996192, tx: 0 state: 1"
+ }
+ ],
+ "currentDifficulty": "0x20000"
+ }
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/13/exp.json b/cmd/evm/testdata/13/exp.json
new file mode 100644
index 0000000000..2b049dfb29
--- /dev/null
+++ b/cmd/evm/testdata/13/exp.json
@@ -0,0 +1,3 @@
+{
+ "body": "0xf8d2b86702f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904b86702f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9"
+}
diff --git a/cmd/evm/testdata/13/exp2.json b/cmd/evm/testdata/13/exp2.json
new file mode 100644
index 0000000000..01ab59e845
--- /dev/null
+++ b/cmd/evm/testdata/13/exp2.json
@@ -0,0 +1,38 @@
+{
+ "result": {
+ "stateRoot": "0xe4b924a6adb5959fccf769d5b7bb2f6359e26d1e76a2443c5a91a36d826aef61",
+ "txRoot": "0x013509c8563d41c0ae4bf38f2d6d19fc6512a1d0d6be045079c8c9f68bf45f9d",
+ "receiptRoot": "0xa532a08aa9f62431d6fe5d924951b8efb86ed3c54d06fee77788c3767dd13420",
+ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "receipts": [
+ {
+ "type": "0x2",
+ "root": "0x",
+ "status": "0x0",
+ "cumulativeGasUsed": "0x84d0",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "logs": null,
+ "transactionHash": "0xa98a24882ea90916c6a86da650fbc6b14238e46f0af04a131ce92be897507476",
+ "contractAddress": "0x0000000000000000000000000000000000000000",
+ "gasUsed": "0x84d0",
+ "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "transactionIndex": "0x0"
+ },
+ {
+ "type": "0x2",
+ "root": "0x",
+ "status": "0x0",
+ "cumulativeGasUsed": "0x109a0",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "logs": null,
+ "transactionHash": "0x36bad80acce7040c45fd32764b5c2b2d2e6f778669fb41791f73f546d56e739a",
+ "contractAddress": "0x0000000000000000000000000000000000000000",
+ "gasUsed": "0x84d0",
+ "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "transactionIndex": "0x1"
+ }
+ ],
+ "currentDifficulty": "0x20000"
+ }
+}
diff --git a/cmd/evm/testdata/13/signed_txs.rlp b/cmd/evm/testdata/13/signed_txs.rlp
new file mode 100644
index 0000000000..9d1157ea45
--- /dev/null
+++ b/cmd/evm/testdata/13/signed_txs.rlp
@@ -0,0 +1 @@
+"0xf8d2b86702f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904b86702f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9"
\ No newline at end of file
diff --git a/cmd/evm/testdata/14/alloc.json b/cmd/evm/testdata/14/alloc.json
new file mode 100644
index 0000000000..cef1a25ff0
--- /dev/null
+++ b/cmd/evm/testdata/14/alloc.json
@@ -0,0 +1,12 @@
+{
+ "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
+ "balance": "0x5ffd4878be161d74",
+ "code": "0x",
+ "nonce": "0xac",
+ "storage": {}
+ },
+ "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192":{
+ "balance": "0xfeedbead",
+ "nonce" : "0x00"
+ }
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/14/env.json b/cmd/evm/testdata/14/env.json
new file mode 100644
index 0000000000..0bf1c5cf48
--- /dev/null
+++ b/cmd/evm/testdata/14/env.json
@@ -0,0 +1,9 @@
+{
+ "currentCoinbase": "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b",
+ "currentGasLimit": "0x750a163df65e8a",
+ "currentBaseFee": "0x500",
+ "currentNumber": "12800000",
+ "currentTimestamp": "100015",
+ "parentTimestamp" : "99999",
+ "parentDifficulty" : "0x2000000000000"
+}
diff --git a/cmd/evm/testdata/14/env.uncles.json b/cmd/evm/testdata/14/env.uncles.json
new file mode 100644
index 0000000000..83811b95ec
--- /dev/null
+++ b/cmd/evm/testdata/14/env.uncles.json
@@ -0,0 +1,10 @@
+{
+ "currentCoinbase": "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b",
+ "currentGasLimit": "0x750a163df65e8a",
+ "currentBaseFee": "0x500",
+ "currentNumber": "12800000",
+ "currentTimestamp": "100035",
+ "parentTimestamp" : "99999",
+ "parentDifficulty" : "0x2000000000000",
+ "parentUncleHash" : "0x000000000000000000000000000000000000000000000000000000000000beef"
+}
diff --git a/cmd/evm/testdata/14/exp.json b/cmd/evm/testdata/14/exp.json
new file mode 100644
index 0000000000..bbe6a1317d
--- /dev/null
+++ b/cmd/evm/testdata/14/exp.json
@@ -0,0 +1,11 @@
+{
+ "result": {
+ "stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
+ "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "currentDifficulty": "0x2000020000000",
+ "receipts": []
+ }
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/14/exp2.json b/cmd/evm/testdata/14/exp2.json
new file mode 100644
index 0000000000..195c738d99
--- /dev/null
+++ b/cmd/evm/testdata/14/exp2.json
@@ -0,0 +1,11 @@
+{
+ "result": {
+ "stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
+ "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "receipts": [],
+ "currentDifficulty": "0x1ff8020000000"
+ }
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/14/exp_berlin.json b/cmd/evm/testdata/14/exp_berlin.json
new file mode 100644
index 0000000000..e564788315
--- /dev/null
+++ b/cmd/evm/testdata/14/exp_berlin.json
@@ -0,0 +1,11 @@
+{
+ "result": {
+ "stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
+ "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "receipts": [],
+ "currentDifficulty": "0x1ff9000000000"
+ }
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/14/readme.md b/cmd/evm/testdata/14/readme.md
new file mode 100644
index 0000000000..9d0dc9569c
--- /dev/null
+++ b/cmd/evm/testdata/14/readme.md
@@ -0,0 +1,41 @@
+## Difficulty calculation
+
+This test shows how the `evm t8n` can be used to calculate the (ethash) difficulty, if none is provided by the caller.
+
+Calculating it (with an empty set of txs) using `London` rules (and no provided unclehash for the parent block):
+```
+[user@work evm]$ ./evm t8n --input.alloc=./testdata/14/alloc.json --input.txs=./testdata/14/txs.json --input.env=./testdata/14/env.json --output.result=stdout --state.fork=London
+INFO [08-30|20:43:09.352] Trie dumping started root=6f0588..7f4bdc
+INFO [08-30|20:43:09.352] Trie dumping complete accounts=2 elapsed="82.533µs"
+INFO [08-30|20:43:09.352] Wrote file file=alloc.json
+{
+ "result": {
+ "stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
+ "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "receipts": [],
+ "currentDifficulty": "0x2000020000000"
+ }
+}
+```
+Same thing, but this time providing a non-empty (and non-`emptyKeccak`) unclehash, which leads to a slightly different result:
+```
+[user@work evm]$ ./evm t8n --input.alloc=./testdata/14/alloc.json --input.txs=./testdata/14/txs.json --input.env=./testdata/14/env.uncles.json --output.result=stdout --state.fork=London
+INFO [08-30|20:44:33.102] Trie dumping started root=6f0588..7f4bdc
+INFO [08-30|20:44:33.102] Trie dumping complete accounts=2 elapsed="72.91µs"
+INFO [08-30|20:44:33.102] Wrote file file=alloc.json
+{
+ "result": {
+ "stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
+ "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "receipts": [],
+ "currentDifficulty": "0x1ff8020000000"
+ }
+}
+```
+
diff --git a/cmd/evm/testdata/14/txs.json b/cmd/evm/testdata/14/txs.json
new file mode 100644
index 0000000000..fe51488c70
--- /dev/null
+++ b/cmd/evm/testdata/14/txs.json
@@ -0,0 +1 @@
+[]
diff --git a/cmd/evm/testdata/15/blockheader.rlp b/cmd/evm/testdata/15/blockheader.rlp
new file mode 100644
index 0000000000..1124e8e2da
--- /dev/null
+++ b/cmd/evm/testdata/15/blockheader.rlp
@@ -0,0 +1 @@
+"0xf901f0a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000940000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000a00000000000000000000000000000000000000000000000000000000000000000b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000007b0101020383010203a00000000000000000000000000000000000000000000000000000000000000000880000000000000000"
\ No newline at end of file
diff --git a/cmd/evm/testdata/15/exp.json b/cmd/evm/testdata/15/exp.json
new file mode 100644
index 0000000000..1893fdfc08
--- /dev/null
+++ b/cmd/evm/testdata/15/exp.json
@@ -0,0 +1,10 @@
+[
+ {
+ "error": "transaction type not supported",
+ "hash": "0xa98a24882ea90916c6a86da650fbc6b14238e46f0af04a131ce92be897507476"
+ },
+ {
+ "error": "transaction type not supported",
+ "hash": "0x36bad80acce7040c45fd32764b5c2b2d2e6f778669fb41791f73f546d56e739a"
+ }
+]
\ No newline at end of file
diff --git a/cmd/evm/testdata/15/exp2.json b/cmd/evm/testdata/15/exp2.json
new file mode 100644
index 0000000000..2c49326ce5
--- /dev/null
+++ b/cmd/evm/testdata/15/exp2.json
@@ -0,0 +1,12 @@
+[
+ {
+ "address": "0xd02d72e067e77158444ef2020ff2d325f929b363",
+ "hash": "0xa98a24882ea90916c6a86da650fbc6b14238e46f0af04a131ce92be897507476",
+ "intrinsicGas": 21000
+ },
+ {
+ "address": "0xd02d72e067e77158444ef2020ff2d325f929b363",
+ "hash": "0x36bad80acce7040c45fd32764b5c2b2d2e6f778669fb41791f73f546d56e739a",
+ "intrinsicGas": 21000
+ }
+]
diff --git a/cmd/evm/testdata/15/exp3.json b/cmd/evm/testdata/15/exp3.json
new file mode 100644
index 0000000000..6c46d267cf
--- /dev/null
+++ b/cmd/evm/testdata/15/exp3.json
@@ -0,0 +1,47 @@
+[
+ {
+ "error": "transaction type not supported"
+ },
+ {
+ "error": "transaction type not supported"
+ },
+ {
+ "error": "transaction type not supported"
+ },
+ {
+ "error": "transaction type not supported"
+ },
+ {
+ "error": "transaction type not supported"
+ },
+ {
+ "error": "transaction type not supported"
+ },
+ {
+ "error": "transaction type not supported"
+ },
+ {
+ "error": "rlp: expected List"
+ },
+ {
+ "error": "rlp: expected List"
+ },
+ {
+ "error": "rlp: expected List"
+ },
+ {
+ "error": "rlp: expected List"
+ },
+ {
+ "error": "rlp: expected List"
+ },
+ {
+ "error": "rlp: expected input list for types.AccessListTx"
+ },
+ {
+ "error": "transaction type not supported"
+ },
+ {
+ "error": "transaction type not supported"
+ }
+]
diff --git a/cmd/evm/testdata/15/signed_txs.rlp b/cmd/evm/testdata/15/signed_txs.rlp
new file mode 100644
index 0000000000..9d1157ea45
--- /dev/null
+++ b/cmd/evm/testdata/15/signed_txs.rlp
@@ -0,0 +1 @@
+"0xf8d2b86702f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904b86702f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9"
\ No newline at end of file
diff --git a/cmd/evm/testdata/15/signed_txs.rlp.json b/cmd/evm/testdata/15/signed_txs.rlp.json
new file mode 100644
index 0000000000..187f40f24a
--- /dev/null
+++ b/cmd/evm/testdata/15/signed_txs.rlp.json
@@ -0,0 +1,4 @@
+{
+ "txsRlp" : "0xf8d2b86702f864010180820fa08284d09411111111111111111111111111111111111111118080c001a0b7dfab36232379bb3d1497a4f91c1966b1f932eae3ade107bf5d723b9cb474e0a06261c359a10f2132f126d250485b90cf20f30340801244a08ef6142ab33d1904b86702f864010280820fa08284d09411111111111111111111111111111111111111118080c080a0d4ec563b6568cd42d998fc4134b36933c6568d01533b5adf08769270243c6c7fa072bf7c21eac6bbeae5143371eef26d5e279637f3bd73482b55979d76d935b1e9"
+}
+
diff --git a/cmd/evm/testdata/16/exp.json b/cmd/evm/testdata/16/exp.json
new file mode 100644
index 0000000000..075c977f28
--- /dev/null
+++ b/cmd/evm/testdata/16/exp.json
@@ -0,0 +1,13 @@
+[
+ {
+ "address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
+ "hash": "0x7cc3d1a8540a44736750f03bb4d85c0113be4b3472a71bf82241a3b261b479e6",
+ "intrinsicGas": 21000
+ },
+ {
+ "error": "intrinsic gas too low: have 82, want 21000",
+ "address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
+ "hash": "0x3b2d2609e4361562edb9169314f4c05afc6dbf5d706bf9dda5abe242ab76a22b",
+ "intrinsicGas": 21000
+ }
+]
\ No newline at end of file
diff --git a/cmd/evm/testdata/16/signed_txs.rlp b/cmd/evm/testdata/16/signed_txs.rlp
new file mode 100644
index 0000000000..952ced2130
--- /dev/null
+++ b/cmd/evm/testdata/16/signed_txs.rlp
@@ -0,0 +1 @@
+"0xf8cab86401f8610180018252089411111111111111111111111111111111111111112080c001a0937f65ef1deece46c473b99962678fb7c38425cf303d1e8fa9717eb4b9d012b5a01940c5a5647c4940217ffde1051a5fd92ec8551e275c1787f81f50a2ad84de43b86201f85f018001529411111111111111111111111111111111111111112080c001a0241c3aec732205542a87fef8c76346741e85480bce5a42d05a9a73dac892f84ca04f52e2dfce57f3a02ed10e085e1a154edf38a726da34127c85fc53b4921759c8"
\ No newline at end of file
diff --git a/cmd/evm/testdata/16/unsigned_txs.json b/cmd/evm/testdata/16/unsigned_txs.json
new file mode 100644
index 0000000000..f619589406
--- /dev/null
+++ b/cmd/evm/testdata/16/unsigned_txs.json
@@ -0,0 +1,34 @@
+[
+ {
+ "input" : "0x",
+ "gas" : "0x5208",
+ "nonce" : "0x0",
+ "to" : "0x1111111111111111111111111111111111111111",
+ "value" : "0x20",
+ "v" : "0x0",
+ "r" : "0x0",
+ "s" : "0x0",
+ "secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
+ "chainId" : "0x1",
+ "type" : "0x1",
+ "gasPrice": "0x1",
+ "accessList" : [
+ ]
+ },
+ {
+ "input" : "0x",
+ "gas" : "0x52",
+ "nonce" : "0x0",
+ "to" : "0x1111111111111111111111111111111111111111",
+ "value" : "0x20",
+ "v" : "0x0",
+ "r" : "0x0",
+ "s" : "0x0",
+ "secretKey" : "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8",
+ "chainId" : "0x1",
+ "type" : "0x1",
+ "gasPrice": "0x1",
+ "accessList" : [
+ ]
+ }
+]
diff --git a/cmd/evm/testdata/17/exp.json b/cmd/evm/testdata/17/exp.json
new file mode 100644
index 0000000000..1c6c54723a
--- /dev/null
+++ b/cmd/evm/testdata/17/exp.json
@@ -0,0 +1,22 @@
+ [
+ {
+ "error": "value exceeds 256 bits",
+ "address": "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b",
+ "hash": "0xfbd91685dcbf8172f0e8c53e2ddbb4d26707840da6b51a74371f62a33868fd82",
+ "intrinsicGas": 21000
+ },
+ {
+ "error": "gasPrice exceeds 256 bits",
+ "address": "0x1b57ccef1fe5fb73f1e64530fb4ebd9cf1655964",
+ "hash": "0x45dc05035cada83748e4c1fe617220106b331eca054f44c2304d5654a9fb29d5",
+ "intrinsicGas": 21000
+ },
+ {
+ "error": "invalid transaction v, r, s values",
+ "hash": "0xf06691c2a803ab7f3c81d06a0c0a896f80f311105c599fc59a9fdbc669356d35"
+ },
+ {
+ "error": "invalid transaction v, r, s values",
+ "hash": "0x84703b697ad5b0db25e4f1f98fb6b1adce85b9edb2232eeba9cedd8c6601694b"
+ }
+]
\ No newline at end of file
diff --git a/cmd/evm/testdata/17/rlpdata.txt b/cmd/evm/testdata/17/rlpdata.txt
new file mode 100644
index 0000000000..874461fd76
--- /dev/null
+++ b/cmd/evm/testdata/17/rlpdata.txt
@@ -0,0 +1,46 @@
+[
+ [
+ "",
+ "d",
+ 5208,
+ d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0,
+ 010000000000000000000000000000000000000000000000000000000000000001,
+ "",
+ 1b,
+ c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549d,
+ 6180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28,
+ ],
+ [
+ "",
+ 010000000000000000000000000000000000000000000000000000000000000001,
+ 5208,
+ d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0,
+ 11,
+ "",
+ 1b,
+ c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549d,
+ 6180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28,
+ ],
+ [
+ "",
+ 11,
+ 5208,
+ d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0,
+ 11,
+ "",
+ 1b,
+ c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549daa,
+ 6180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28,
+ ],
+ [
+ "",
+ 11,
+ 5208,
+ d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0,
+ 11,
+ "",
+ 1b,
+ c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549d,
+ 6180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28bb,
+ ],
+]
diff --git a/cmd/evm/testdata/17/signed_txs.rlp b/cmd/evm/testdata/17/signed_txs.rlp
new file mode 100644
index 0000000000..0e351fb03c
--- /dev/null
+++ b/cmd/evm/testdata/17/signed_txs.rlp
@@ -0,0 +1 @@
+"0xf901c8f880806482520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0a1010000000000000000000000000000000000000000000000000000000000000001801ba0c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549da06180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28f88080a101000000000000000000000000000000000000000000000000000000000000000182520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d011801ba0c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549da06180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28f860801182520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d011801ba1c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549daaa06180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28f860801182520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d011801ba0c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549da16180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28bb"
\ No newline at end of file
diff --git a/cmd/evm/testdata/18/README.md b/cmd/evm/testdata/18/README.md
new file mode 100644
index 0000000000..360a9bba01
--- /dev/null
+++ b/cmd/evm/testdata/18/README.md
@@ -0,0 +1,9 @@
+# Invalid rlp
+
+This folder contains a sample of invalid RLP, and it's expected
+that the t9n handles this properly:
+
+```
+$ go run . t9n --input.txs=./testdata/18/invalid.rlp --state.fork=London
+ERROR(11): rlp: value size exceeds available input length
+```
\ No newline at end of file
diff --git a/cmd/evm/testdata/18/invalid.rlp b/cmd/evm/testdata/18/invalid.rlp
new file mode 100644
index 0000000000..7ff2824caf
--- /dev/null
+++ b/cmd/evm/testdata/18/invalid.rlp
@@ -0,0 +1 @@
+"0xf852328001825208870b9331677e6ebf0a801ca098ff921201554726367d2be8c804a7ff89ccf285ebc57dff8ae4c44b9c19ac4aa03887321be575c8095f789dd4c743dfe42c1820f9231f98a962b210e3ac2452a3"
\ No newline at end of file
diff --git a/cmd/evm/testdata/19/alloc.json b/cmd/evm/testdata/19/alloc.json
new file mode 100644
index 0000000000..cef1a25ff0
--- /dev/null
+++ b/cmd/evm/testdata/19/alloc.json
@@ -0,0 +1,12 @@
+{
+ "a94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
+ "balance": "0x5ffd4878be161d74",
+ "code": "0x",
+ "nonce": "0xac",
+ "storage": {}
+ },
+ "0x8a8eafb1cf62bfbeb1741769dae1a9dd47996192":{
+ "balance": "0xfeedbead",
+ "nonce" : "0x00"
+ }
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/19/env.json b/cmd/evm/testdata/19/env.json
new file mode 100644
index 0000000000..0c64392aff
--- /dev/null
+++ b/cmd/evm/testdata/19/env.json
@@ -0,0 +1,9 @@
+{
+ "currentCoinbase": "0xc94f5374fce5edbc8e2a8697c15331677e6ebf0b",
+ "currentGasLimit": "0x750a163df65e8a",
+ "currentBaseFee": "0x500",
+ "currentNumber": "13000000",
+ "currentTimestamp": "100015",
+ "parentTimestamp" : "99999",
+ "parentDifficulty" : "0x2000000000000"
+}
diff --git a/cmd/evm/testdata/19/exp_arrowglacier.json b/cmd/evm/testdata/19/exp_arrowglacier.json
new file mode 100644
index 0000000000..4c5f8e0fbd
--- /dev/null
+++ b/cmd/evm/testdata/19/exp_arrowglacier.json
@@ -0,0 +1,11 @@
+{
+ "result": {
+ "stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
+ "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "currentDifficulty": "0x2000000200000",
+ "receipts": []
+ }
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/19/exp_london.json b/cmd/evm/testdata/19/exp_london.json
new file mode 100644
index 0000000000..9dc1b9d4f3
--- /dev/null
+++ b/cmd/evm/testdata/19/exp_london.json
@@ -0,0 +1,11 @@
+{
+ "result": {
+ "stateRoot": "0x6f058887ca01549716789c380ede95aecc510e6d1fdc4dbf67d053c7c07f4bdc",
+ "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "currentDifficulty": "0x2000080000000",
+ "receipts": []
+ }
+}
\ No newline at end of file
diff --git a/cmd/evm/testdata/19/readme.md b/cmd/evm/testdata/19/readme.md
new file mode 100644
index 0000000000..5fae183f48
--- /dev/null
+++ b/cmd/evm/testdata/19/readme.md
@@ -0,0 +1,9 @@
+## Difficulty calculation
+
+This test shows how the `evm t8n` can be used to calculate the (ethash) difficulty, if none is provided by the caller,
+this time on `ArrowGlacier` (Eip 4345).
+
+Calculating it (with an empty set of txs) using `ArrowGlacier` rules (and no provided unclehash for the parent block):
+```
+[user@work evm]$ ./evm t8n --input.alloc=./testdata/14/alloc.json --input.txs=./testdata/14/txs.json --input.env=./testdata/14/env.json --output.result=stdout --state.fork=ArrowGlacier
+```
\ No newline at end of file
diff --git a/cmd/evm/testdata/19/txs.json b/cmd/evm/testdata/19/txs.json
new file mode 100644
index 0000000000..fe51488c70
--- /dev/null
+++ b/cmd/evm/testdata/19/txs.json
@@ -0,0 +1 @@
+[]
diff --git a/cmd/evm/testdata/3/exp.json b/cmd/evm/testdata/3/exp.json
new file mode 100644
index 0000000000..ade09e9ac1
--- /dev/null
+++ b/cmd/evm/testdata/3/exp.json
@@ -0,0 +1,37 @@
+{
+ "alloc": {
+ "0x095e7baea6a6c7c4c2dfeb977efac326af552d87": {
+ "code": "0x600140",
+ "balance": "0xde0b6b3a76586a0"
+ },
+ "0x2adc25665018aa1fe0e6bc666dac8fc2697ff9ba": {
+ "balance": "0x521f"
+ },
+ "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
+ "balance": "0xde0b6b3a7622741",
+ "nonce": "0x1"
+ }
+ },
+ "result": {
+ "stateRoot": "0xb7341da3f9f762a6884eaa186c32942734c146b609efee11c4b0214c44857ea1",
+ "txRoot": "0x75e61774a2ff58cbe32653420256c7f44bc715715a423b0b746d5c622979af6b",
+ "receiptRoot": "0xd0d26df80374a327c025d405ebadc752b1bbd089d864801ae78ab704bcad8086",
+ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "receipts": [
+ {
+ "root": "0x",
+ "status": "0x1",
+ "cumulativeGasUsed": "0x521f",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "logs": null,
+ "transactionHash": "0x72fadbef39cd251a437eea619cfeda752271a5faaaa2147df012e112159ffb81",
+ "contractAddress": "0x0000000000000000000000000000000000000000",
+ "gasUsed": "0x521f",
+ "blockHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
+ "transactionIndex": "0x0"
+ }
+ ],
+ "currentDifficulty": "0x20000"
+ }
+}
diff --git a/cmd/evm/testdata/5/exp.json b/cmd/evm/testdata/5/exp.json
new file mode 100644
index 0000000000..6340d4cc30
--- /dev/null
+++ b/cmd/evm/testdata/5/exp.json
@@ -0,0 +1,22 @@
+{
+ "alloc": {
+ "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa": {
+ "balance": "0x88"
+ },
+ "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb": {
+ "balance": "0x70"
+ },
+ "0xcccccccccccccccccccccccccccccccccccccccc": {
+ "balance": "0x60"
+ }
+ },
+ "result": {
+ "stateRoot": "0xa7312add33811645c6aa65d928a1a4f49d65d448801912c069a0aa8fe9c1f393",
+ "txRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "receiptRoot": "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
+ "logsHash": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "logsBloom": "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
+ "receipts": [],
+ "currentDifficulty": "0x20000"
+ }
+}
diff --git a/cmd/faucet/faucet.go b/cmd/faucet/faucet.go
index bb5375384f..2d9d7a1e99 100644
--- a/cmd/faucet/faucet.go
+++ b/cmd/faucet/faucet.go
@@ -741,7 +741,7 @@ func authTwitter(url string, tokenV1, tokenV2 string) (string, string, string, c
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 {
+ if parts = regexp.MustCompile(`src="([^"]+twimg\.com/profile_images[^"]+)"`).FindStringSubmatch(string(body)); len(parts) == 2 {
avatar = parts[1]
}
return username + "@twitter", username, avatar, address, nil
@@ -867,7 +867,7 @@ func authFacebook(url string) (string, string, common.Address, error) {
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 {
+ if parts = regexp.MustCompile(`src="([^"]+fbcdn\.net[^"]+)"`).FindStringSubmatch(string(body)); len(parts) == 2 {
avatar = parts[1]
}
return username + "@facebook", avatar, address, nil
diff --git a/cmd/geth/accountcmd.go b/cmd/geth/accountcmd.go
index 6d45c88763..e33b9eb0fb 100644
--- a/cmd/geth/accountcmd.go
+++ b/cmd/geth/accountcmd.go
@@ -268,11 +268,16 @@ func accountCreate(ctx *cli.Context) error {
}
}
utils.SetNodeConfig(ctx, &cfg.Node)
- scryptN, scryptP, keydir, err := cfg.Node.AccountConfig()
-
+ keydir, err := cfg.Node.KeyDirConfig()
if err != nil {
utils.Fatalf("Failed to read configuration: %v", err)
}
+ scryptN := keystore.StandardScryptN
+ scryptP := keystore.StandardScryptP
+ if cfg.Node.UseLightweightKDF {
+ scryptN = keystore.LightScryptN
+ scryptP = keystore.LightScryptP
+ }
password := utils.GetPassPhraseWithList("Your new account is locked with a password. Please give a password. Do not forget this password.", true, 0, utils.MakePasswordList(ctx))
diff --git a/cmd/geth/config.go b/cmd/geth/config.go
index 604f907b7f..08b9a1154f 100644
--- a/cmd/geth/config.go
+++ b/cmd/geth/config.go
@@ -27,6 +27,10 @@ import (
"gopkg.in/urfave/cli.v1"
+ "github.com/ethereum/go-ethereum/accounts/external"
+ "github.com/ethereum/go-ethereum/accounts/keystore"
+ "github.com/ethereum/go-ethereum/accounts/scwallet"
+ "github.com/ethereum/go-ethereum/accounts/usbwallet"
"github.com/ethereum/go-ethereum/cmd/utils"
"github.com/ethereum/go-ethereum/eth/catalyst"
"github.com/ethereum/go-ethereum/eth/ethconfig"
@@ -135,6 +139,11 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
if err != nil {
utils.Fatalf("Failed to create the protocol stack: %v", err)
}
+ // Node doesn't by default populate account manager backends
+ if err := setAccountManagerBackends(stack); err != nil {
+ utils.Fatalf("Failed to set account manager backends: %v", err)
+ }
+
utils.SetEthConfig(ctx, stack, &cfg.Eth)
if ctx.GlobalIsSet(utils.EthStatsURLFlag.Name) {
cfg.Ethstats.URL = ctx.GlobalString(utils.EthStatsURLFlag.Name)
@@ -147,8 +156,8 @@ func makeConfigNode(ctx *cli.Context) (*node.Node, gethConfig) {
// makeFullNode loads geth configuration and creates the Ethereum backend.
func makeFullNode(ctx *cli.Context) (*node.Node, ethapi.Backend) {
stack, cfg := makeConfigNode(ctx)
- if ctx.GlobalIsSet(utils.OverrideLondonFlag.Name) {
- cfg.Eth.OverrideLondon = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideLondonFlag.Name))
+ if ctx.GlobalIsSet(utils.OverrideArrowGlacierFlag.Name) {
+ cfg.Eth.OverrideArrowGlacier = new(big.Int).SetUint64(ctx.GlobalUint64(utils.OverrideArrowGlacierFlag.Name))
}
backend, eth := utils.RegisterEthService(stack, &cfg.Eth)
@@ -257,3 +266,62 @@ func deprecated(field string) bool {
return false
}
}
+
+func setAccountManagerBackends(stack *node.Node) error {
+ conf := stack.Config()
+ am := stack.AccountManager()
+ keydir := stack.KeyStoreDir()
+ scryptN := keystore.StandardScryptN
+ scryptP := keystore.StandardScryptP
+ if conf.UseLightweightKDF {
+ scryptN = keystore.LightScryptN
+ scryptP = keystore.LightScryptP
+ }
+
+ // Assemble the supported backends
+ if len(conf.ExternalSigner) > 0 {
+ log.Info("Using external signer", "url", conf.ExternalSigner)
+ if extapi, err := external.NewExternalBackend(conf.ExternalSigner); err == nil {
+ am.AddBackend(extapi)
+ return nil
+ } else {
+ return fmt.Errorf("error connecting to external signer: %v", err)
+ }
+ }
+
+ // For now, we're using EITHER external signer OR local signers.
+ // If/when we implement some form of lockfile for USB and keystore wallets,
+ // we can have both, but it's very confusing for the user to see the same
+ // accounts in both externally and locally, plus very racey.
+ am.AddBackend(keystore.NewKeyStore(keydir, scryptN, scryptP))
+ if conf.USB {
+ // Start a USB hub for Ledger hardware wallets
+ if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil {
+ log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err))
+ } else {
+ am.AddBackend(ledgerhub)
+ }
+ // Start a USB hub for Trezor hardware wallets (HID version)
+ if trezorhub, err := usbwallet.NewTrezorHubWithHID(); err != nil {
+ log.Warn(fmt.Sprintf("Failed to start HID Trezor hub, disabling: %v", err))
+ } else {
+ am.AddBackend(trezorhub)
+ }
+ // Start a USB hub for Trezor hardware wallets (WebUSB version)
+ if trezorhub, err := usbwallet.NewTrezorHubWithWebUSB(); err != nil {
+ log.Warn(fmt.Sprintf("Failed to start WebUSB Trezor hub, disabling: %v", err))
+ } else {
+ am.AddBackend(trezorhub)
+ }
+ }
+ if len(conf.SmartCardDaemonPath) > 0 {
+ // Start a smart card hub
+ if schub, err := scwallet.NewHub(conf.SmartCardDaemonPath, scwallet.Scheme, keydir); err != nil {
+ log.Warn(fmt.Sprintf("Failed to start smart card hub, disabling: %v", err))
+ } else {
+ am.AddBackend(schub)
+ }
+ }
+
+ return nil
+}
diff --git a/cmd/geth/consolecmd_test.go b/cmd/geth/consolecmd_test.go
index c3f41b187c..845ede2f9c 100644
--- a/cmd/geth/consolecmd_test.go
+++ b/cmd/geth/consolecmd_test.go
@@ -75,7 +75,7 @@ at block: 0 ({{niltime}})
datadir: {{.Datadir}}
modules: {{apis}}
-To exit, press ctrl-d
+To exit, press ctrl-d or type exit
> {{.InputLine "exit"}}
`)
geth.ExpectExit()
@@ -149,7 +149,7 @@ at block: 0 ({{niltime}}){{if ipc}}
datadir: {{datadir}}{{end}}
modules: {{apis}}
-To exit, press ctrl-d
+To exit, press ctrl-d or type exit
> {{.InputLine "exit" }}
`)
attach.ExpectExit()
diff --git a/cmd/geth/dbcmd.go b/cmd/geth/dbcmd.go
index 54ada40890..123ed9c798 100644
--- a/cmd/geth/dbcmd.go
+++ b/cmd/geth/dbcmd.go
@@ -17,6 +17,7 @@
package main
import (
+ "errors"
"fmt"
"os"
"path/filepath"
@@ -335,14 +336,15 @@ func dbGet(ctx *cli.Context) error {
db := utils.MakeChainDatabase(ctx, stack, true)
defer db.Close()
- key, err := hexutil.Decode(ctx.Args().Get(0))
+ key, err := parseHexOrString(ctx.Args().Get(0))
if err != nil {
log.Info("Could not decode the key", "error", err)
return err
}
+
data, err := db.Get(key)
if err != nil {
- log.Info("Get operation failed", "error", err)
+ log.Info("Get operation failed", "key", fmt.Sprintf("0x%#x", key), "error", err)
return err
}
fmt.Printf("key %#x: %#x\n", key, data)
@@ -360,7 +362,7 @@ func dbDelete(ctx *cli.Context) error {
db := utils.MakeChainDatabase(ctx, stack, false)
defer db.Close()
- key, err := hexutil.Decode(ctx.Args().Get(0))
+ key, err := parseHexOrString(ctx.Args().Get(0))
if err != nil {
log.Info("Could not decode the key", "error", err)
return err
@@ -370,7 +372,7 @@ func dbDelete(ctx *cli.Context) error {
fmt.Printf("Previous value: %#x\n", data)
}
if err = db.Delete(key); err != nil {
- log.Info("Delete operation returned an error", "error", err)
+ log.Info("Delete operation returned an error", "key", fmt.Sprintf("0x%#x", key), "error", err)
return err
}
return nil
@@ -393,7 +395,7 @@ func dbPut(ctx *cli.Context) error {
data []byte
err error
)
- key, err = hexutil.Decode(ctx.Args().Get(0))
+ key, err = parseHexOrString(ctx.Args().Get(0))
if err != nil {
log.Info("Could not decode the key", "error", err)
return err
@@ -499,3 +501,12 @@ func freezerInspect(ctx *cli.Context) error {
}
return nil
}
+
+// ParseHexOrString tries to hexdecode b, but if the prefix is missing, it instead just returns the raw bytes
+func parseHexOrString(str string) ([]byte, error) {
+ b, err := hexutil.Decode(str)
+ if errors.Is(err, hexutil.ErrMissingPrefix) {
+ return []byte(str), nil
+ }
+ return b, err
+}
diff --git a/cmd/geth/main.go b/cmd/geth/main.go
index da62b8d4d4..9d80ce0bed 100644
--- a/cmd/geth/main.go
+++ b/cmd/geth/main.go
@@ -66,7 +66,7 @@ var (
utils.NoUSBFlag,
utils.USBFlag,
utils.SmartCardDaemonPathFlag,
- utils.OverrideLondonFlag,
+ utils.OverrideArrowGlacierFlag,
utils.EthashCacheDirFlag,
utils.EthashCachesInMemoryFlag,
utils.EthashCachesOnDiskFlag,
@@ -124,7 +124,7 @@ var (
utils.MinerEtherbaseFlag,
utils.MinerExtraDataFlag,
utils.MinerRecommitIntervalFlag,
- utils.MinerNoVerfiyFlag,
+ utils.MinerNoVerifyFlag,
utils.NATFlag,
utils.NoDiscoverFlag,
utils.DiscoveryV5Flag,
@@ -158,12 +158,6 @@ var (
utils.HTTPPortFlag,
utils.HTTPCORSDomainFlag,
utils.HTTPVirtualHostsFlag,
- utils.LegacyRPCEnabledFlag,
- utils.LegacyRPCListenAddrFlag,
- utils.LegacyRPCPortFlag,
- utils.LegacyRPCCORSDomainFlag,
- utils.LegacyRPCVirtualHostsFlag,
- utils.LegacyRPCApiFlag,
utils.GraphQLEnabledFlag,
utils.GraphQLCORSDomainFlag,
utils.GraphQLVirtualHostsFlag,
@@ -179,6 +173,7 @@ var (
utils.IPCPathFlag,
utils.InsecureUnlockAllowedFlag,
utils.RPCGlobalGasCapFlag,
+ utils.RPCGlobalEVMTimeoutFlag,
utils.RPCGlobalTxFeeCapFlag,
utils.AllowUnprotectedTxs,
}
@@ -410,7 +405,7 @@ func startNode(ctx *cli.Context, stack *node.Node, backend ethapi.Backend) {
}
ethBackend, ok := backend.(*eth.EthAPIBackend)
if !ok {
- utils.Fatalf("Ethereum service not running: %v", err)
+ utils.Fatalf("Ethereum service not running")
}
// Set the gas price to the limits from the CLI and start mining
gasprice := utils.GlobalBig(ctx, utils.MinerGasPriceFlag.Name)
diff --git a/cmd/geth/snapshot.go b/cmd/geth/snapshot.go
index 35d027fb16..d3903e0af4 100644
--- a/cmd/geth/snapshot.go
+++ b/cmd/geth/snapshot.go
@@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/pruner"
"github.com/ethereum/go-ethereum/core/state/snapshot"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
@@ -232,7 +233,7 @@ func verifyState(ctx *cli.Context) error {
}
}
if err := snaptree.Verify(root); err != nil {
- log.Error("Failed to verfiy state", "root", root, "err", err)
+ log.Error("Failed to verify state", "root", root, "err", err)
return err
}
log.Info("Verified the state", "root", root)
@@ -287,7 +288,7 @@ func traverseState(ctx *cli.Context) error {
accIter := trie.NewIterator(t.NodeIterator(nil))
for accIter.Next() {
accounts += 1
- var acc state.Account
+ var acc types.StateAccount
if err := rlp.DecodeBytes(accIter.Value, &acc); err != nil {
log.Error("Invalid account encountered during traversal", "err", err)
return err
@@ -393,7 +394,7 @@ func traverseRawState(ctx *cli.Context) error {
// dig into the storage trie further.
if accIter.Leaf() {
accounts += 1
- var acc state.Account
+ var acc types.StateAccount
if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil {
log.Error("Invalid account encountered during traversal", "err", err)
return errors.New("invalid account")
diff --git a/cmd/geth/usage.go b/cmd/geth/usage.go
index 708edcc793..5d82baceaa 100644
--- a/cmd/geth/usage.go
+++ b/cmd/geth/usage.go
@@ -150,6 +150,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
utils.GraphQLCORSDomainFlag,
utils.GraphQLVirtualHostsFlag,
utils.RPCGlobalGasCapFlag,
+ utils.RPCGlobalEVMTimeoutFlag,
utils.RPCGlobalTxFeeCapFlag,
utils.AllowUnprotectedTxs,
utils.JSpathFlag,
@@ -185,7 +186,7 @@ var AppHelpFlagGroups = []flags.FlagGroup{
utils.MinerEtherbaseFlag,
utils.MinerExtraDataFlag,
utils.MinerRecommitIntervalFlag,
- utils.MinerNoVerfiyFlag,
+ utils.MinerNoVerifyFlag,
},
},
{
@@ -218,13 +219,6 @@ var AppHelpFlagGroups = []flags.FlagGroup{
Name: "ALIASED (deprecated)",
Flags: []cli.Flag{
utils.NoUSBFlag,
- utils.LegacyRPCEnabledFlag,
- utils.LegacyRPCListenAddrFlag,
- utils.LegacyRPCPortFlag,
- utils.LegacyRPCCORSDomainFlag,
- utils.LegacyRPCVirtualHostsFlag,
- utils.LegacyRPCApiFlag,
- utils.LegacyMinerGasTargetFlag,
},
},
{
diff --git a/cmd/puppeth/module_explorer.go b/cmd/puppeth/module_explorer.go
index 3ce9d612b9..1165f70fcf 100644
--- a/cmd/puppeth/module_explorer.go
+++ b/cmd/puppeth/module_explorer.go
@@ -35,8 +35,8 @@ FROM puppeth/blockscout:latest
ADD genesis.json /genesis.json
RUN \
echo 'geth --cache 512 init /genesis.json' > explorer.sh && \
- echo $'geth --networkid {{.NetworkID}} --syncmode "full" --gcmode "archive" --port {{.EthPort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --http --http.api "net,web3,eth,shh,debug" --http.corsdomain "*" --http.vhosts "*" --ws --ws.origins "*" --exitwhensynced' >> explorer.sh && \
- echo $'exec geth --networkid {{.NetworkID}} --syncmode "full" --gcmode "archive" --port {{.EthPort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --http --http.api "net,web3,eth,shh,debug" --http.corsdomain "*" --http.vhosts "*" --ws --ws.origins "*" &' >> explorer.sh && \
+ echo $'geth --networkid {{.NetworkID}} --syncmode "full" --gcmode "archive" --port {{.EthPort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --http --http.api "net,web3,eth,debug,txpool" --http.corsdomain "*" --http.vhosts "*" --ws --ws.origins "*" --exitwhensynced' >> explorer.sh && \
+ echo $'exec geth --networkid {{.NetworkID}} --syncmode "full" --gcmode "archive" --port {{.EthPort}} --bootnodes {{.Bootnodes}} --ethstats \'{{.Ethstats}}\' --cache=512 --http --http.api "net,web3,eth,debug,txpool" --http.corsdomain "*" --http.vhosts "*" --ws --ws.origins "*" &' >> explorer.sh && \
echo '/usr/local/bin/docker-entrypoint.sh postgres &' >> explorer.sh && \
echo 'sleep 5' >> explorer.sh && \
echo 'mix do ecto.drop --force, ecto.create, ecto.migrate' >> explorer.sh && \
diff --git a/cmd/puppeth/wizard.go b/cmd/puppeth/wizard.go
index 83536506c4..c0edc54019 100644
--- a/cmd/puppeth/wizard.go
+++ b/cmd/puppeth/wizard.go
@@ -17,7 +17,6 @@
package main
import (
- "bufio"
"encoding/json"
"fmt"
"io/ioutil"
@@ -32,8 +31,10 @@ import (
"sync"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/console/prompt"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/log"
+ "github.com/peterh/liner"
"golang.org/x/crypto/ssh/terminal"
)
@@ -76,17 +77,27 @@ type wizard struct {
servers map[string]*sshClient // SSH connections to servers to administer
services map[string][]string // Ethereum services known to be running on servers
- in *bufio.Reader // Wrapper around stdin to allow reading user input
- lock sync.Mutex // Lock to protect configs during concurrent service discovery
+ lock sync.Mutex // Lock to protect configs during concurrent service discovery
+}
+
+// prompts the user for input with the given prompt string. Returns when a value is entered.
+// Causes the wizard to exit if ctrl-d is pressed
+func promptInput(p string) string {
+ for {
+ text, err := prompt.Stdin.PromptInput(p)
+ if err != nil {
+ if err != liner.ErrPromptAborted {
+ log.Crit("Failed to read user input", "err", err)
+ }
+ } else {
+ return text
+ }
+ }
}
// read reads a single line from stdin, trimming if from spaces.
func (w *wizard) read() string {
- fmt.Printf("> ")
- text, err := w.in.ReadString('\n')
- if err != nil {
- log.Crit("Failed to read user input", "err", err)
- }
+ text := promptInput("> ")
return strings.TrimSpace(text)
}
@@ -94,11 +105,7 @@ func (w *wizard) read() string {
// non-emptyness.
func (w *wizard) readString() string {
for {
- fmt.Printf("> ")
- text, err := w.in.ReadString('\n')
- if err != nil {
- log.Crit("Failed to read user input", "err", err)
- }
+ text := promptInput("> ")
if text = strings.TrimSpace(text); text != "" {
return text
}
@@ -108,11 +115,7 @@ func (w *wizard) readString() string {
// readDefaultString reads a single line from stdin, trimming if from spaces. If
// an empty line is entered, the default value is returned.
func (w *wizard) readDefaultString(def string) string {
- fmt.Printf("> ")
- text, err := w.in.ReadString('\n')
- if err != nil {
- log.Crit("Failed to read user input", "err", err)
- }
+ text := promptInput("> ")
if text = strings.TrimSpace(text); text != "" {
return text
}
@@ -124,11 +127,7 @@ func (w *wizard) readDefaultString(def string) string {
// value is returned.
func (w *wizard) readDefaultYesNo(def bool) bool {
for {
- fmt.Printf("> ")
- text, err := w.in.ReadString('\n')
- if err != nil {
- log.Crit("Failed to read user input", "err", err)
- }
+ text := promptInput("> ")
if text = strings.ToLower(strings.TrimSpace(text)); text == "" {
return def
}
@@ -146,11 +145,7 @@ func (w *wizard) readDefaultYesNo(def bool) bool {
// interpret it as a URL (http, https or file).
func (w *wizard) readURL() *url.URL {
for {
- fmt.Printf("> ")
- text, err := w.in.ReadString('\n')
- if err != nil {
- log.Crit("Failed to read user input", "err", err)
- }
+ text := promptInput("> ")
uri, err := url.Parse(strings.TrimSpace(text))
if err != nil {
log.Error("Invalid input, expected URL", "err", err)
@@ -164,11 +159,7 @@ func (w *wizard) readURL() *url.URL {
// to parse into an integer.
func (w *wizard) readInt() int {
for {
- fmt.Printf("> ")
- text, err := w.in.ReadString('\n')
- if err != nil {
- log.Crit("Failed to read user input", "err", err)
- }
+ text := promptInput("> ")
if text = strings.TrimSpace(text); text == "" {
continue
}
@@ -186,11 +177,7 @@ func (w *wizard) readInt() int {
// returned.
func (w *wizard) readDefaultInt(def int) int {
for {
- fmt.Printf("> ")
- text, err := w.in.ReadString('\n')
- if err != nil {
- log.Crit("Failed to read user input", "err", err)
- }
+ text := promptInput("> ")
if text = strings.TrimSpace(text); text == "" {
return def
}
@@ -208,11 +195,7 @@ func (w *wizard) readDefaultInt(def int) int {
// default value is returned.
func (w *wizard) readDefaultBigInt(def *big.Int) *big.Int {
for {
- fmt.Printf("> ")
- text, err := w.in.ReadString('\n')
- if err != nil {
- log.Crit("Failed to read user input", "err", err)
- }
+ text := promptInput("> ")
if text = strings.TrimSpace(text); text == "" {
return def
}
@@ -225,38 +208,11 @@ func (w *wizard) readDefaultBigInt(def *big.Int) *big.Int {
}
}
-/*
-// readFloat reads a single line from stdin, trimming if from spaces, enforcing it
-// to parse into a float.
-func (w *wizard) readFloat() float64 {
- for {
- fmt.Printf("> ")
- text, err := w.in.ReadString('\n')
- if err != nil {
- log.Crit("Failed to read user input", "err", err)
- }
- if text = strings.TrimSpace(text); text == "" {
- continue
- }
- val, err := strconv.ParseFloat(strings.TrimSpace(text), 64)
- if err != nil {
- log.Error("Invalid input, expected float", "err", err)
- continue
- }
- return val
- }
-}
-*/
-
// readDefaultFloat reads a single line from stdin, trimming if from spaces, enforcing
// it to parse into a float. If an empty line is entered, the default value is returned.
func (w *wizard) readDefaultFloat(def float64) float64 {
for {
- fmt.Printf("> ")
- text, err := w.in.ReadString('\n')
- if err != nil {
- log.Crit("Failed to read user input", "err", err)
- }
+ text := promptInput("> ")
if text = strings.TrimSpace(text); text == "" {
return def
}
@@ -285,12 +241,7 @@ func (w *wizard) readPassword() string {
// it to an Ethereum address.
func (w *wizard) readAddress() *common.Address {
for {
- // Read the address from the user
- fmt.Printf("> 0x")
- text, err := w.in.ReadString('\n')
- if err != nil {
- log.Crit("Failed to read user input", "err", err)
- }
+ text := promptInput("> 0x")
if text = strings.TrimSpace(text); text == "" {
return nil
}
@@ -311,11 +262,7 @@ func (w *wizard) readAddress() *common.Address {
func (w *wizard) readDefaultAddress(def common.Address) common.Address {
for {
// Read the address from the user
- fmt.Printf("> 0x")
- text, err := w.in.ReadString('\n')
- if err != nil {
- log.Crit("Failed to read user input", "err", err)
- }
+ text := promptInput("> 0x")
if text = strings.TrimSpace(text); text == "" {
return def
}
@@ -334,8 +281,9 @@ func (w *wizard) readJSON() string {
var blob json.RawMessage
for {
- fmt.Printf("> ")
- if err := json.NewDecoder(w.in).Decode(&blob); err != nil {
+ text := promptInput("> ")
+ reader := strings.NewReader(text)
+ if err := json.NewDecoder(reader).Decode(&blob); err != nil {
log.Error("Invalid JSON, please try again", "err", err)
continue
}
@@ -351,10 +299,7 @@ func (w *wizard) readIPAddress() string {
for {
// Read the IP address from the user
fmt.Printf("> ")
- text, err := w.in.ReadString('\n')
- if err != nil {
- log.Crit("Failed to read user input", "err", err)
- }
+ text := promptInput("> ")
if text = strings.TrimSpace(text); text == "" {
return ""
}
diff --git a/cmd/puppeth/wizard_intro.go b/cmd/puppeth/wizard_intro.go
index 75fb04b76f..8610b908d8 100644
--- a/cmd/puppeth/wizard_intro.go
+++ b/cmd/puppeth/wizard_intro.go
@@ -17,7 +17,6 @@
package main
import (
- "bufio"
"encoding/json"
"fmt"
"io/ioutil"
@@ -38,7 +37,6 @@ func makeWizard(network string) *wizard {
},
servers: make(map[string]*sshClient),
services: make(map[string][]string),
- in: bufio.NewReader(os.Stdin),
}
}
diff --git a/cmd/rlpdump/main.go b/cmd/rlpdump/main.go
index d0f993c5b8..9c0af01248 100644
--- a/cmd/rlpdump/main.go
+++ b/cmd/rlpdump/main.go
@@ -18,7 +18,9 @@
package main
import (
+ "bufio"
"bytes"
+ "container/list"
"encoding/hex"
"flag"
"fmt"
@@ -26,18 +28,20 @@ import (
"os"
"strings"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/rlp"
)
var (
- hexMode = flag.String("hex", "", "dump given hex data")
- noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably")
- single = flag.Bool("single", false, "print only the first element, discard the rest")
+ hexMode = flag.String("hex", "", "dump given hex data")
+ reverseMode = flag.Bool("reverse", false, "convert ASCII to rlp")
+ noASCII = flag.Bool("noascii", false, "don't print ASCII strings readably")
+ single = flag.Bool("single", false, "print only the first element, discard the rest")
)
func init() {
flag.Usage = func() {
- fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[-noascii] [-hex ] [filename]")
+ fmt.Fprintln(os.Stderr, "Usage:", os.Args[0], "[-noascii] [-hex ][-reverse] [filename]")
flag.PrintDefaults()
fmt.Fprintln(os.Stderr, `
Dumps RLP data from the given file in readable form.
@@ -73,23 +77,40 @@ func main() {
flag.Usage()
os.Exit(2)
}
-
- s := rlp.NewStream(r, 0)
- for {
- if err := dump(s, 0); err != nil {
- if err != io.EOF {
- die(err)
- }
- break
+ out := os.Stdout
+ if *reverseMode {
+ data, err := textToRlp(r)
+ if err != nil {
+ die(err)
}
- fmt.Println()
- if *single {
- break
+ fmt.Printf("0x%x\n", data)
+ return
+ } else {
+ err := rlpToText(r, out)
+ if err != nil {
+ die(err)
}
}
}
-func dump(s *rlp.Stream, depth int) error {
+func rlpToText(r io.Reader, out io.Writer) error {
+ s := rlp.NewStream(r, 0)
+ for {
+ if err := dump(s, 0, out); err != nil {
+ if err != io.EOF {
+ return err
+ }
+ break
+ }
+ fmt.Fprintln(out)
+ if *single {
+ break
+ }
+ }
+ return nil
+}
+
+func dump(s *rlp.Stream, depth int, out io.Writer) error {
kind, size, err := s.Kind()
if err != nil {
return err
@@ -101,28 +122,28 @@ func dump(s *rlp.Stream, depth int) error {
return err
}
if len(str) == 0 || !*noASCII && isASCII(str) {
- fmt.Printf("%s%q", ws(depth), str)
+ fmt.Fprintf(out, "%s%q", ws(depth), str)
} else {
- fmt.Printf("%s%x", ws(depth), str)
+ fmt.Fprintf(out, "%s%x", ws(depth), str)
}
case rlp.List:
s.List()
defer s.ListEnd()
if size == 0 {
- fmt.Print(ws(depth) + "[]")
+ fmt.Fprintf(out, ws(depth)+"[]")
} else {
- fmt.Println(ws(depth) + "[")
+ fmt.Fprintln(out, ws(depth)+"[")
for i := 0; ; i++ {
if i > 0 {
- fmt.Print(",\n")
+ fmt.Fprint(out, ",\n")
}
- if err := dump(s, depth+1); err == rlp.EOL {
+ if err := dump(s, depth+1, out); err == rlp.EOL {
break
} else if err != nil {
return err
}
}
- fmt.Print(ws(depth) + "]")
+ fmt.Fprint(out, ws(depth)+"]")
}
}
return nil
@@ -145,3 +166,45 @@ func die(args ...interface{}) {
fmt.Fprintln(os.Stderr, args...)
os.Exit(1)
}
+
+// textToRlp converts text into RLP (best effort).
+func textToRlp(r io.Reader) ([]byte, error) {
+ // We're expecting the input to be well-formed, meaning that
+ // - each element is on a separate line
+ // - each line is either an (element OR a list start/end) + comma
+ // - an element is either hex-encoded bytes OR a quoted string
+ var (
+ scanner = bufio.NewScanner(r)
+ obj []interface{}
+ stack = list.New()
+ )
+ for scanner.Scan() {
+ t := strings.TrimSpace(scanner.Text())
+ if len(t) == 0 {
+ continue
+ }
+ switch t {
+ case "[": // list start
+ stack.PushFront(obj)
+ obj = make([]interface{}, 0)
+ case "]", "],": // list end
+ parent := stack.Remove(stack.Front()).([]interface{})
+ obj = append(parent, obj)
+ case "[],": // empty list
+ obj = append(obj, make([]interface{}, 0))
+ default: // element
+ data := []byte(t)[:len(t)-1] // cut off comma
+ if data[0] == '"' { // ascii string
+ data = []byte(t)[1 : len(data)-1]
+ } else { // hex data
+ data = common.FromHex(string(data))
+ }
+ obj = append(obj, data)
+ }
+ }
+ if err := scanner.Err(); err != nil {
+ return nil, err
+ }
+ data, err := rlp.EncodeToBytes(obj[0])
+ return data, err
+}
diff --git a/cmd/rlpdump/rlpdump_test.go b/cmd/rlpdump/rlpdump_test.go
new file mode 100644
index 0000000000..ea607e3808
--- /dev/null
+++ b/cmd/rlpdump/rlpdump_test.go
@@ -0,0 +1,65 @@
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "strings"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/hexutil"
+)
+
+func TestRoundtrip(t *testing.T) {
+ for i, want := range []string{
+ "0xf880806482520894d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0d0a1010000000000000000000000000000000000000000000000000000000000000001801ba0c16787a8e25e941d67691954642876c08f00996163ae7dfadbbfd6cd436f549da06180e5626cae31590f40641fe8f63734316c4bfeb4cdfab6714198c1044d2e28",
+ "0xd5c0d3cb84746573742a2a808213378667617a6f6e6b",
+ "0xc780c0c1c0825208",
+ } {
+ var out strings.Builder
+ err := rlpToText(bytes.NewReader(common.FromHex(want)), &out)
+ if err != nil {
+ t.Fatal(err)
+ }
+ text := out.String()
+ rlpBytes, err := textToRlp(strings.NewReader(text))
+ if err != nil {
+ t.Errorf("test %d: error %v", i, err)
+ continue
+ }
+ have := fmt.Sprintf("0x%x", rlpBytes)
+ if have != want {
+ t.Errorf("test %d: have\n%v\nwant:\n%v\n", i, have, want)
+ }
+ }
+}
+
+func TestTextToRlp(t *testing.T) {
+ type tc struct {
+ text string
+ want string
+ }
+ cases := []tc{
+ {
+ text: `[
+ "",
+ [],
+[
+ [],
+ ],
+ 5208,
+]`,
+ want: "0xc780c0c1c0825208",
+ },
+ }
+ for i, tc := range cases {
+ have, err := textToRlp(strings.NewReader(tc.text))
+ if err != nil {
+ t.Errorf("test %d: error %v", i, err)
+ continue
+ }
+ if hexutil.Encode(have) != tc.want {
+ t.Errorf("test %d:\nhave %v\nwant %v", i, hexutil.Encode(have), tc.want)
+ }
+ }
+}
diff --git a/cmd/utils/diskusage.go b/cmd/utils/diskusage.go
index c3d51765f8..09844652ef 100644
--- a/cmd/utils/diskusage.go
+++ b/cmd/utils/diskusage.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 !windows && !openbsd
// +build !windows,!openbsd
package utils
diff --git a/cmd/utils/diskusage_openbsd.go b/cmd/utils/diskusage_openbsd.go
index 54f759d291..52502d0cfa 100644
--- a/cmd/utils/diskusage_openbsd.go
+++ b/cmd/utils/diskusage_openbsd.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 openbsd
// +build openbsd
package utils
diff --git a/cmd/utils/flags.go b/cmd/utils/flags.go
index 8a53f525f6..b3d9f395b0 100644
--- a/cmd/utils/flags.go
+++ b/cmd/utils/flags.go
@@ -235,9 +235,9 @@ var (
Usage: "Megabytes of memory allocated to bloom-filter for pruning",
Value: 2048,
}
- OverrideLondonFlag = cli.Uint64Flag{
- Name: "override.london",
- Usage: "Manually specify London fork-block, overriding the bundled setting",
+ OverrideArrowGlacierFlag = cli.Uint64Flag{
+ Name: "override.arrowglacier",
+ Usage: "Manually specify Arrow Glacier fork-block, overriding the bundled setting",
}
// Light server and client settings
LightServeFlag = cli.IntFlag{
@@ -460,7 +460,7 @@ var (
Usage: "Time interval to recreate the block being mined",
Value: ethconfig.Defaults.Miner.Recommit,
}
- MinerNoVerfiyFlag = cli.BoolFlag{
+ MinerNoVerifyFlag = cli.BoolFlag{
Name: "miner.noverify",
Usage: "Disable remote sealing verification",
}
@@ -493,6 +493,11 @@ var (
Usage: "Sets a cap on gas that can be used in eth_call/estimateGas (0=infinite)",
Value: ethconfig.Defaults.RPCGasCap,
}
+ RPCGlobalEVMTimeoutFlag = cli.DurationFlag{
+ Name: "rpc.evmtimeout",
+ Usage: "Sets a timeout used for eth_call (0=infinite)",
+ Value: ethconfig.Defaults.RPCEVMTimeout,
+ }
RPCGlobalTxFeeCapFlag = 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)",
@@ -681,7 +686,7 @@ var (
}
GpoMaxGasPriceFlag = cli.Int64Flag{
Name: "gpo.maxprice",
- Usage: "Maximum gas price will be recommended by gpo",
+ Usage: "Maximum transaction priority fee (or gasprice before London fork) to be recommended by gpo",
Value: ethconfig.Defaults.GPO.MaxPrice.Int64(),
}
GpoIgnoreGasPriceFlag = cli.Int64Flag{
@@ -920,14 +925,6 @@ func SplitAndTrim(input string) (ret []string) {
// 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(LegacyRPCEnabledFlag.Name) && cfg.HTTPHost == "" {
- log.Warn("The flag --rpc is deprecated and will be removed June 2021, please use --http")
- cfg.HTTPHost = "127.0.0.1"
- if ctx.GlobalIsSet(LegacyRPCListenAddrFlag.Name) {
- cfg.HTTPHost = ctx.GlobalString(LegacyRPCListenAddrFlag.Name)
- log.Warn("The flag --rpcaddr is deprecated and will be removed June 2021, please use --http.addr")
- }
- }
if ctx.GlobalBool(HTTPEnabledFlag.Name) && cfg.HTTPHost == "" {
cfg.HTTPHost = "127.0.0.1"
if ctx.GlobalIsSet(HTTPListenAddrFlag.Name) {
@@ -935,34 +932,18 @@ func setHTTP(ctx *cli.Context, cfg *node.Config) {
}
}
- if ctx.GlobalIsSet(LegacyRPCPortFlag.Name) {
- cfg.HTTPPort = ctx.GlobalInt(LegacyRPCPortFlag.Name)
- log.Warn("The flag --rpcport is deprecated and will be removed June 2021, please use --http.port")
- }
if ctx.GlobalIsSet(HTTPPortFlag.Name) {
cfg.HTTPPort = ctx.GlobalInt(HTTPPortFlag.Name)
}
- if ctx.GlobalIsSet(LegacyRPCCORSDomainFlag.Name) {
- cfg.HTTPCors = SplitAndTrim(ctx.GlobalString(LegacyRPCCORSDomainFlag.Name))
- log.Warn("The flag --rpccorsdomain is deprecated and will be removed June 2021, please use --http.corsdomain")
- }
if ctx.GlobalIsSet(HTTPCORSDomainFlag.Name) {
cfg.HTTPCors = SplitAndTrim(ctx.GlobalString(HTTPCORSDomainFlag.Name))
}
- if ctx.GlobalIsSet(LegacyRPCApiFlag.Name) {
- cfg.HTTPModules = SplitAndTrim(ctx.GlobalString(LegacyRPCApiFlag.Name))
- log.Warn("The flag --rpcapi is deprecated and will be removed June 2021, please use --http.api")
- }
if ctx.GlobalIsSet(HTTPApiFlag.Name) {
cfg.HTTPModules = SplitAndTrim(ctx.GlobalString(HTTPApiFlag.Name))
}
- if ctx.GlobalIsSet(LegacyRPCVirtualHostsFlag.Name) {
- cfg.HTTPVirtualHosts = SplitAndTrim(ctx.GlobalString(LegacyRPCVirtualHostsFlag.Name))
- log.Warn("The flag --rpcvhosts is deprecated and will be removed June 2021, please use --http.vhosts")
- }
if ctx.GlobalIsSet(HTTPVirtualHostsFlag.Name) {
cfg.HTTPVirtualHosts = SplitAndTrim(ctx.GlobalString(HTTPVirtualHostsFlag.Name))
}
@@ -1398,8 +1379,8 @@ func setMiner(ctx *cli.Context, cfg *miner.Config) {
if ctx.GlobalIsSet(MinerRecommitIntervalFlag.Name) {
cfg.Recommit = ctx.GlobalDuration(MinerRecommitIntervalFlag.Name)
}
- if ctx.GlobalIsSet(MinerNoVerfiyFlag.Name) {
- cfg.Noverify = ctx.GlobalBool(MinerNoVerfiyFlag.Name)
+ if ctx.GlobalIsSet(MinerNoVerifyFlag.Name) {
+ cfg.Noverify = ctx.GlobalBool(MinerNoVerifyFlag.Name)
}
if ctx.GlobalIsSet(LegacyMinerGasTargetFlag.Name) {
log.Warn("The generic --miner.gastarget flag is deprecated and will be removed in the future!")
@@ -1587,6 +1568,9 @@ func SetEthConfig(ctx *cli.Context, stack *node.Node, cfg *ethconfig.Config) {
} else {
log.Info("Global gas cap disabled")
}
+ if ctx.GlobalIsSet(RPCGlobalEVMTimeoutFlag.Name) {
+ cfg.RPCEVMTimeout = ctx.GlobalDuration(RPCGlobalEVMTimeoutFlag.Name)
+ }
if ctx.GlobalIsSet(RPCGlobalTxFeeCapFlag.Name) {
cfg.RPCTxFeeCap = ctx.GlobalFloat64(RPCGlobalTxFeeCapFlag.Name)
}
diff --git a/cmd/utils/flags_legacy.go b/cmd/utils/flags_legacy.go
index 627f774753..a0f64f609b 100644
--- a/cmd/utils/flags_legacy.go
+++ b/cmd/utils/flags_legacy.go
@@ -18,10 +18,8 @@ package utils
import (
"fmt"
- "strings"
"github.com/ethereum/go-ethereum/eth/ethconfig"
- "github.com/ethereum/go-ethereum/node"
"gopkg.in/urfave/cli.v1"
)
@@ -45,35 +43,6 @@ var (
Name: "nousb",
Usage: "Disables monitoring for and managing USB hardware wallets (deprecated)",
}
- LegacyRPCEnabledFlag = cli.BoolFlag{
- Name: "rpc",
- Usage: "Enable the HTTP-RPC server (deprecated and will be removed June 2021, use --http)",
- }
- LegacyRPCListenAddrFlag = cli.StringFlag{
- Name: "rpcaddr",
- Usage: "HTTP-RPC server listening interface (deprecated and will be removed June 2021, use --http.addr)",
- Value: node.DefaultHTTPHost,
- }
- LegacyRPCPortFlag = cli.IntFlag{
- Name: "rpcport",
- Usage: "HTTP-RPC server listening port (deprecated and will be removed June 2021, use --http.port)",
- Value: node.DefaultHTTPPort,
- }
- LegacyRPCCORSDomainFlag = cli.StringFlag{
- Name: "rpccorsdomain",
- Usage: "Comma separated list of domains from which to accept cross origin requests (browser enforced) (deprecated and will be removed June 2021, use --http.corsdomain)",
- Value: "",
- }
- LegacyRPCVirtualHostsFlag = cli.StringFlag{
- Name: "rpcvhosts",
- Usage: "Comma separated list of virtual hostnames from which to accept requests (server enforced). Accepts '*' wildcard. (deprecated and will be removed June 2021, use --http.vhosts)",
- Value: strings.Join(node.DefaultConfig.HTTPVirtualHosts, ","),
- }
- LegacyRPCApiFlag = cli.StringFlag{
- Name: "rpcapi",
- Usage: "API's offered over the HTTP-RPC interface (deprecated and will be removed June 2021, use --http.api)",
- Value: "",
- }
// (Deprecated July 2021, shown in aliased flags section)
LegacyMinerGasTargetFlag = cli.Uint64Flag{
Name: "miner.gastarget",
diff --git a/common/fdlimit/fdlimit_bsd.go b/common/fdlimit/fdlimit_bsd.go
index 86181337a2..a3a6902c09 100644
--- a/common/fdlimit/fdlimit_bsd.go
+++ b/common/fdlimit/fdlimit_bsd.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 freebsd || dragonfly
// +build freebsd dragonfly
package fdlimit
diff --git a/common/fdlimit/fdlimit_unix.go b/common/fdlimit/fdlimit_unix.go
index e5a575f7a7..a1f388ebb7 100644
--- a/common/fdlimit/fdlimit_unix.go
+++ b/common/fdlimit/fdlimit_unix.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 || netbsd || openbsd || solaris
// +build linux netbsd openbsd solaris
package fdlimit
diff --git a/common/hexutil/hexutil.go b/common/hexutil/hexutil.go
index 46223a2815..e0241f5f2b 100644
--- a/common/hexutil/hexutil.go
+++ b/common/hexutil/hexutil.go
@@ -176,13 +176,14 @@ func MustDecodeBig(input string) *big.Int {
}
// EncodeBig encodes bigint as a hex string with 0x prefix.
-// The sign of the integer is ignored.
func EncodeBig(bigint *big.Int) string {
- nbits := bigint.BitLen()
- if nbits == 0 {
+ if sign := bigint.Sign(); sign == 0 {
return "0x0"
+ } else if sign > 0 {
+ return "0x" + bigint.Text(16)
+ } else {
+ return "-0x" + bigint.Text(16)[1:]
}
- return fmt.Sprintf("%#x", bigint)
}
func has0xPrefix(input string) bool {
diff --git a/common/hexutil/hexutil_test.go b/common/hexutil/hexutil_test.go
index ed6fccc3ca..f2b800d82c 100644
--- a/common/hexutil/hexutil_test.go
+++ b/common/hexutil/hexutil_test.go
@@ -201,3 +201,15 @@ func TestDecodeUint64(t *testing.T) {
}
}
}
+
+func BenchmarkEncodeBig(b *testing.B) {
+ for _, bench := range encodeBigTests {
+ b.Run(bench.want, func(b *testing.B) {
+ b.ReportAllocs()
+ bigint := bench.input.(*big.Int)
+ for i := 0; i < b.N; i++ {
+ EncodeBig(bigint)
+ }
+ })
+ }
+}
diff --git a/common/types.go b/common/types.go
index d715356692..2205835cb5 100644
--- a/common/types.go
+++ b/common/types.go
@@ -86,7 +86,7 @@ func (h Hash) String() string {
}
// Format implements fmt.Formatter.
-// Hash supports the %v, %s, %v, %x, %X and %d format verbs.
+// Hash supports the %v, %s, %q, %x, %X and %d format verbs.
func (h Hash) Format(s fmt.State, c rune) {
hexb := make([]byte, 2+len(h)*2)
copy(hexb, "0x")
@@ -270,7 +270,7 @@ func (a Address) hex() []byte {
}
// Format implements fmt.Formatter.
-// Address supports the %v, %s, %v, %x, %X and %d format verbs.
+// Address supports the %v, %s, %q, %x, %X and %d format verbs.
func (a Address) Format(s fmt.State, c rune) {
switch c {
case 'v', 's':
diff --git a/consensus/clique/api.go b/consensus/clique/api.go
index 6129b5cc52..03f2daffa0 100644
--- a/consensus/clique/api.go
+++ b/consensus/clique/api.go
@@ -214,6 +214,9 @@ func (api *API) GetSigner(rlpOrBlockNr *blockNumberOrHashOrRLP) (common.Address,
} else if number, ok := blockNrOrHash.Number(); ok {
header = api.chain.GetHeaderByNumber(uint64(number.Int64()))
}
+ if header == nil {
+ return common.Address{}, fmt.Errorf("missing block %v", blockNrOrHash.String())
+ }
return api.clique.Author(header)
}
block := new(types.Block)
diff --git a/consensus/clique/clique.go b/consensus/clique/clique.go
index 449095e723..a6a16c84af 100644
--- a/consensus/clique/clique.go
+++ b/consensus/clique/clique.go
@@ -363,7 +363,7 @@ func (c *Clique) verifyCascadingFields(chain consensus.ChainHeaderReader, header
}
}
// All basic checks passed, verify the seal and return
- return c.verifySeal(chain, header, parents)
+ return c.verifySeal(snap, header, parents)
}
// snapshot retrieves the authorization snapshot at a given point in time.
@@ -460,18 +460,12 @@ func (c *Clique) VerifyUncles(chain consensus.ChainReader, block *types.Block) e
// consensus protocol requirements. The method accepts an optional list of parent
// headers that aren't yet part of the local blockchain to generate the snapshots
// from.
-func (c *Clique) verifySeal(chain consensus.ChainHeaderReader, header *types.Header, parents []*types.Header) error {
+func (c *Clique) verifySeal(snap *Snapshot, header *types.Header, parents []*types.Header) error {
// Verifying the genesis block is not supported
number := header.Number.Uint64()
if number == 0 {
return errUnknownBlock
}
- // Retrieve the snapshot needed to verify this header and cache it
- snap, err := c.snapshot(chain, number-1, header.ParentHash, parents)
- if err != nil {
- return err
- }
-
// Resolve the authorization key and check against signers
signer, err := ecrecover(header, c.signatures)
if err != nil {
diff --git a/consensus/ethash/consensus.go b/consensus/ethash/consensus.go
index 5743387f6c..7fa427f68b 100644
--- a/consensus/ethash/consensus.go
+++ b/consensus/ethash/consensus.go
@@ -45,6 +45,11 @@ var (
maxUncles = 2 // Maximum number of uncles allowed in a single block
allowedFutureBlockTimeSeconds = int64(15) // Max seconds from current time allowed for blocks, before they're considered future blocks
+ // calcDifficultyEip4345 is the difficulty adjustment algorithm as specified by EIP 4345.
+ // It offsets the bomb a total of 10.7M blocks.
+ // Specification EIP-4345: https://eips.ethereum.org/EIPS/eip-4345
+ calcDifficultyEip4345 = makeDifficultyCalculator(big.NewInt(10_700_000))
+
// calcDifficultyEip3554 is the difficulty adjustment algorithm as specified by EIP 3554.
// It offsets the bomb a total of 9.7M blocks.
// Specification EIP-3554: https://eips.ethereum.org/EIPS/eip-3554
@@ -330,8 +335,8 @@ func (ethash *Ethash) CalcDifficulty(chain consensus.ChainHeaderReader, time uin
func CalcDifficulty(config *params.ChainConfig, time uint64, parent *types.Header) *big.Int {
next := new(big.Int).Add(parent.Number, big1)
switch {
- case config.IsCatalyst(next):
- return big.NewInt(1)
+ case config.IsArrowGlacier(next):
+ return calcDifficultyEip4345(time, parent)
case config.IsLondon(next):
return calcDifficultyEip3554(time, parent)
case config.IsMuirGlacier(next):
@@ -639,10 +644,6 @@ var (
// reward. The total reward consists of the static block reward and rewards for
// included uncles. The coinbase of each uncle block is also rewarded.
func accumulateRewards(config *params.ChainConfig, state *state.StateDB, header *types.Header, uncles []*types.Header) {
- // Skip block reward in catalyst mode
- if config.IsCatalyst(header.Number) {
- return
- }
// Select the correct block reward based on chain progression
blockReward := FrontierBlockReward
if config.IsByzantium(header.Number) {
diff --git a/consensus/misc/eip1559_test.go b/consensus/misc/eip1559_test.go
index fd400b6881..23cd9023de 100644
--- a/consensus/misc/eip1559_test.go
+++ b/consensus/misc/eip1559_test.go
@@ -29,24 +29,24 @@ import (
// do not use e.g. SetInt() on the numbers. For testing only
func copyConfig(original *params.ChainConfig) *params.ChainConfig {
return ¶ms.ChainConfig{
- ChainID: original.ChainID,
- HomesteadBlock: original.HomesteadBlock,
- DAOForkBlock: original.DAOForkBlock,
- DAOForkSupport: original.DAOForkSupport,
- EIP150Block: original.EIP150Block,
- EIP150Hash: original.EIP150Hash,
- EIP155Block: original.EIP155Block,
- EIP158Block: original.EIP158Block,
- ByzantiumBlock: original.ByzantiumBlock,
- ConstantinopleBlock: original.ConstantinopleBlock,
- PetersburgBlock: original.PetersburgBlock,
- IstanbulBlock: original.IstanbulBlock,
- MuirGlacierBlock: original.MuirGlacierBlock,
- BerlinBlock: original.BerlinBlock,
- LondonBlock: original.LondonBlock,
- CatalystBlock: original.CatalystBlock,
- Ethash: original.Ethash,
- Clique: original.Clique,
+ ChainID: original.ChainID,
+ HomesteadBlock: original.HomesteadBlock,
+ DAOForkBlock: original.DAOForkBlock,
+ DAOForkSupport: original.DAOForkSupport,
+ EIP150Block: original.EIP150Block,
+ EIP150Hash: original.EIP150Hash,
+ EIP155Block: original.EIP155Block,
+ EIP158Block: original.EIP158Block,
+ ByzantiumBlock: original.ByzantiumBlock,
+ ConstantinopleBlock: original.ConstantinopleBlock,
+ PetersburgBlock: original.PetersburgBlock,
+ IstanbulBlock: original.IstanbulBlock,
+ MuirGlacierBlock: original.MuirGlacierBlock,
+ BerlinBlock: original.BerlinBlock,
+ LondonBlock: original.LondonBlock,
+ TerminalTotalDifficulty: original.TerminalTotalDifficulty,
+ Ethash: original.Ethash,
+ Clique: original.Clique,
}
}
diff --git a/console/console.go b/console/console.go
index ae9f28da04..dd39300d0a 100644
--- a/console/console.go
+++ b/console/console.go
@@ -324,7 +324,7 @@ func (c *Console) Welcome() {
sort.Strings(modules)
message += " modules: " + strings.Join(modules, " ") + "\n"
}
- message += "\nTo exit, press ctrl-d"
+ message += "\nTo exit, press ctrl-d or type exit"
fmt.Fprintln(c.printer, message)
}
diff --git a/core/bench_test.go b/core/bench_test.go
index ce288d372e..959979763d 100644
--- a/core/bench_test.go
+++ b/core/bench_test.go
@@ -75,7 +75,7 @@ var (
// This is the content of the genesis block used by the benchmarks.
benchRootKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
benchRootAddr = crypto.PubkeyToAddress(benchRootKey.PublicKey)
- benchRootFunds = math.BigPow(2, 100)
+ benchRootFunds = math.BigPow(2, 200)
)
// genValueTx returns a block generator that includes a single
@@ -86,7 +86,19 @@ func genValueTx(nbytes int) func(int, *BlockGen) {
toaddr := common.Address{}
data := make([]byte, nbytes)
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)
+ signer := types.MakeSigner(gen.config, big.NewInt(int64(i)))
+ gasPrice := big.NewInt(0)
+ if gen.header.BaseFee != nil {
+ gasPrice = gen.header.BaseFee
+ }
+ tx, _ := types.SignNewTx(benchRootKey, signer, &types.LegacyTx{
+ Nonce: gen.TxNonce(benchRootAddr),
+ To: &toaddr,
+ Value: big.NewInt(1),
+ Gas: gas,
+ Data: data,
+ GasPrice: gasPrice,
+ })
gen.AddTx(tx)
}
}
@@ -110,24 +122,38 @@ func init() {
// and fills the blocks with many small transactions.
func genTxRing(naccounts int) func(int, *BlockGen) {
from := 0
+ availableFunds := new(big.Int).Set(benchRootFunds)
return func(i int, gen *BlockGen) {
block := gen.PrevBlock(i - 1)
gas := block.GasLimit()
+ gasPrice := big.NewInt(0)
+ if gen.header.BaseFee != nil {
+ gasPrice = gen.header.BaseFee
+ }
+ signer := types.MakeSigner(gen.config, big.NewInt(int64(i)))
for {
gas -= params.TxGas
if gas < params.TxGas {
break
}
to := (from + 1) % naccounts
- tx := types.NewTransaction(
- gen.TxNonce(ringAddrs[from]),
- ringAddrs[to],
- benchRootFunds,
- params.TxGas,
- nil,
- nil,
- )
- tx, _ = types.SignTx(tx, types.HomesteadSigner{}, ringKeys[from])
+ burn := new(big.Int).SetUint64(params.TxGas)
+ burn.Mul(burn, gen.header.BaseFee)
+ availableFunds.Sub(availableFunds, burn)
+ if availableFunds.Cmp(big.NewInt(1)) < 0 {
+ panic("not enough funds")
+ }
+ tx, err := types.SignNewTx(ringKeys[from], signer,
+ &types.LegacyTx{
+ Nonce: gen.TxNonce(ringAddrs[from]),
+ To: &ringAddrs[to],
+ Value: availableFunds,
+ Gas: params.TxGas,
+ GasPrice: gasPrice,
+ })
+ if err != nil {
+ panic(err)
+ }
gen.AddTx(tx)
from = to
}
@@ -245,6 +271,7 @@ func makeChainForBench(db ethdb.Database, full bool, count uint64) {
block := types.NewBlockWithHeader(header)
rawdb.WriteBody(db, hash, n, block.Body())
rawdb.WriteReceipts(db, hash, n, nil)
+ rawdb.WriteHeadBlockHash(db, hash)
}
}
}
@@ -278,6 +305,8 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
}
makeChainForBench(db, full, count)
db.Close()
+ cacheConfig := *defaultCacheConfig
+ cacheConfig.TrieDirtyDisabled = true
b.ReportAllocs()
b.ResetTimer()
@@ -287,7 +316,7 @@ func benchReadChain(b *testing.B, full bool, count uint64) {
if err != nil {
b.Fatalf("error opening database at %v: %v", dir, err)
}
- chain, err := NewBlockChain(db, nil, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
+ chain, err := NewBlockChain(db, &cacheConfig, params.TestChainConfig, ethash.NewFaker(), vm.Config{}, nil, nil)
if err != nil {
b.Fatalf("error creating chain: %v", err)
}
diff --git a/core/blockchain.go b/core/blockchain.go
index e14291aa54..ff372870dd 100644
--- a/core/blockchain.go
+++ b/core/blockchain.go
@@ -39,10 +39,10 @@ import (
"github.com/ethereum/go-ethereum/core/vm"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/internal/syncx"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/params"
- "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
lru "github.com/hashicorp/golang-lru"
)
@@ -80,6 +80,7 @@ var (
blockPrefetchInterruptMeter = metrics.NewRegisteredMeter("chain/prefetch/interrupts", nil)
errInsertionInterrupted = errors.New("insertion is interrupted")
+ errChainStopped = errors.New("blockchain is stopped")
)
const (
@@ -183,7 +184,9 @@ type BlockChain struct {
scope event.SubscriptionScope
genesisBlock *types.Block
- chainmu sync.RWMutex // blockchain insertion lock
+ // This mutex synchronizes chain write operations.
+ // Readers don't need to take it, they can just read the database.
+ chainmu *syncx.ClosableMutex
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!)
@@ -196,8 +199,8 @@ type BlockChain struct {
txLookupCache *lru.Cache // Cache for the most recent transaction lookup data.
futureBlocks *lru.Cache // future blocks are blocks added for later processing
- quit chan struct{} // blockchain quit channel
- wg sync.WaitGroup // chain processing wait group for shutting down
+ wg sync.WaitGroup //
+ quit chan struct{} // shutdown signal, closed in Stop.
running int32 // 0 if chain is running, 1 when stopped
procInterrupt int32 // interrupt signaler for block processing
@@ -207,8 +210,7 @@ type BlockChain struct {
processor Processor // Block transaction processor interface
vmConfig vm.Config
- shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block.
- terminateInsert func(common.Hash, uint64) bool // Testing hook used to terminate ancient receipt chain insertion.
+ shouldPreserve func(*types.Block) bool // Function used to determine whether should preserve the given block.
}
// NewBlockChain returns a fully initialised block chain using information
@@ -236,6 +238,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
Preimages: cacheConfig.Preimages,
}),
quit: make(chan struct{}),
+ chainmu: syncx.NewClosableMutex(),
shouldPreserve: shouldPreserve,
bodyCache: bodyCache,
bodyRLPCache: bodyRLPCache,
@@ -279,6 +282,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
if err := bc.loadLastState(); err != nil {
return nil, err
}
+
// Make sure the state associated with the block is available
head := bc.CurrentBlock()
if _, err := state.New(head.Root(), bc.stateCache, bc.snaps); err != nil {
@@ -307,6 +311,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
}
}
}
+
// Ensure that a previous crash in SetHead doesn't leave extra ancients
if frozen, err := bc.db.Ancients(); err == nil && frozen > 0 {
var (
@@ -358,6 +363,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
}
}
}
+
// Load any existing snapshot, regenerating it if loading failed
if bc.cacheConfig.SnapshotLimit > 0 {
// If the chain was rewound past the snapshot persistent layer (causing
@@ -373,14 +379,19 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
}
bc.snaps, _ = snapshot.New(bc.db, bc.stateCache.TrieDB(), bc.cacheConfig.SnapshotLimit, head.Root(), !bc.cacheConfig.SnapshotWait, true, recover)
}
- // Take ownership of this particular state
- go bc.update()
+
+ // Start future block processor.
+ bc.wg.Add(1)
+ go bc.futureBlocksLoop()
+
+ // Start tx indexer/unindexer.
if txLookupLimit != nil {
bc.txLookupLimit = *txLookupLimit
bc.wg.Add(1)
go bc.maintainTxIndex(txIndexBlock)
}
+
// If periodic cache journal is required, spin it up.
if bc.cacheConfig.TrieCleanRejournal > 0 {
if bc.cacheConfig.TrieCleanRejournal < time.Minute {
@@ -397,11 +408,6 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
return bc, nil
}
-// GetVMConfig returns the block chain VM config.
-func (bc *BlockChain) GetVMConfig() *vm.Config {
- return &bc.vmConfig
-}
-
// empty returns an indicator whether the blockchain is empty.
// Note, it's a special case that we connect a non-empty ancient
// database with an empty node, so that we can plugin the ancient
@@ -489,7 +495,9 @@ func (bc *BlockChain) SetHead(head uint64) error {
//
// The method returns the block number where the requested root cap was found.
func (bc *BlockChain) SetHeadBeyondRoot(head uint64, root common.Hash) (uint64, error) {
- bc.chainmu.Lock()
+ if !bc.chainmu.TryLock() {
+ return 0, errChainStopped
+ }
defer bc.chainmu.Unlock()
// Track the block number of the requested root hash
@@ -634,8 +642,11 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
if _, err := trie.NewSecure(block.Root(), bc.stateCache.TrieDB()); err != nil {
return err
}
- // If all checks out, manually set the head block
- bc.chainmu.Lock()
+
+ // If all checks out, manually set the head block.
+ if !bc.chainmu.TryLock() {
+ return errChainStopped
+ }
bc.currentBlock.Store(block)
headBlockGauge.Update(int64(block.NumberU64()))
bc.chainmu.Unlock()
@@ -649,53 +660,6 @@ func (bc *BlockChain) FastSyncCommitHead(hash common.Hash) error {
return nil
}
-// GasLimit returns the gas limit of the current HEAD block.
-func (bc *BlockChain) GasLimit() uint64 {
- return bc.CurrentBlock().GasLimit()
-}
-
-// CurrentBlock retrieves the current head block of the canonical chain. The
-// block is retrieved from the blockchain's internal cache.
-func (bc *BlockChain) CurrentBlock() *types.Block {
- return bc.currentBlock.Load().(*types.Block)
-}
-
-// Snapshots returns the blockchain snapshot tree.
-func (bc *BlockChain) Snapshots() *snapshot.Tree {
- return bc.snaps
-}
-
-// CurrentFastBlock retrieves the current fast-sync head block of the canonical
-// chain. The block is retrieved from the blockchain's internal cache.
-func (bc *BlockChain) CurrentFastBlock() *types.Block {
- return bc.currentFastBlock.Load().(*types.Block)
-}
-
-// Validator returns the current validator.
-func (bc *BlockChain) Validator() Validator {
- return bc.validator
-}
-
-// Processor returns the current processor.
-func (bc *BlockChain) Processor() Processor {
- return bc.processor
-}
-
-// State returns a new mutable state based on the current HEAD block.
-func (bc *BlockChain) State() (*state.StateDB, error) {
- return bc.StateAt(bc.CurrentBlock().Root())
-}
-
-// StateAt returns a new mutable state based on a particular point in time.
-func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
- return state.New(root, bc.stateCache, bc.snaps)
-}
-
-// StateCache returns the caching database underpinning the blockchain instance.
-func (bc *BlockChain) StateCache() state.Database {
- return bc.stateCache
-}
-
// Reset purges the entire blockchain, restoring it to its genesis state.
func (bc *BlockChain) Reset() error {
return bc.ResetWithGenesisBlock(bc.genesisBlock)
@@ -708,7 +672,9 @@ func (bc *BlockChain) ResetWithGenesisBlock(genesis *types.Block) error {
if err := bc.SetHead(0); err != nil {
return err
}
- bc.chainmu.Lock()
+ if !bc.chainmu.TryLock() {
+ return errChainStopped
+ }
defer bc.chainmu.Unlock()
// Prepare the genesis block and reinitialise the chain
@@ -738,8 +704,10 @@ func (bc *BlockChain) Export(w io.Writer) error {
// ExportN writes a subset of the active chain to the given writer.
func (bc *BlockChain) ExportN(w io.Writer, first uint64, last uint64) error {
- bc.chainmu.RLock()
- defer bc.chainmu.RUnlock()
+ if !bc.chainmu.TryLock() {
+ return errChainStopped
+ }
+ defer bc.chainmu.Unlock()
if first > last {
return fmt.Errorf("export failed: first (%d) is greater than last (%d)", first, last)
@@ -798,204 +766,27 @@ func (bc *BlockChain) writeHeadBlock(block *types.Block) {
headBlockGauge.Update(int64(block.NumberU64()))
}
-// Genesis retrieves the chain's genesis block.
-func (bc *BlockChain) Genesis() *types.Block {
- return bc.genesisBlock
-}
-
-// GetBody retrieves a block body (transactions and uncles) from the database by
-// hash, caching it if found.
-func (bc *BlockChain) GetBody(hash common.Hash) *types.Body {
- // Short circuit if the body's already in the cache, retrieve otherwise
- if cached, ok := bc.bodyCache.Get(hash); ok {
- body := cached.(*types.Body)
- return body
- }
- number := bc.hc.GetBlockNumber(hash)
- if number == nil {
- return nil
- }
- body := rawdb.ReadBody(bc.db, hash, *number)
- if body == nil {
- return nil
- }
- // Cache the found body for next time and return
- bc.bodyCache.Add(hash, body)
- return body
-}
-
-// GetBodyRLP retrieves a block body in RLP encoding from the database by hash,
-// caching it if found.
-func (bc *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue {
- // Short circuit if the body's already in the cache, retrieve otherwise
- if cached, ok := bc.bodyRLPCache.Get(hash); ok {
- return cached.(rlp.RawValue)
- }
- number := bc.hc.GetBlockNumber(hash)
- if number == nil {
- return nil
- }
- body := rawdb.ReadBodyRLP(bc.db, hash, *number)
- if len(body) == 0 {
- return nil
- }
- // Cache the found body for next time and return
- bc.bodyRLPCache.Add(hash, body)
- return body
-}
-
-// HasBlock checks if a block is fully present in the database or not.
-func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool {
- if bc.blockCache.Contains(hash) {
- return true
- }
- return rawdb.HasBody(bc.db, hash, number)
-}
-
-// HasFastBlock checks if a fast block is fully present in the database or not.
-func (bc *BlockChain) HasFastBlock(hash common.Hash, number uint64) bool {
- if !bc.HasBlock(hash, number) {
- return false
- }
- if bc.receiptsCache.Contains(hash) {
- return true
- }
- return rawdb.HasReceipts(bc.db, hash, number)
-}
-
-// HasState checks if state trie is fully present in the database or not.
-func (bc *BlockChain) HasState(hash common.Hash) bool {
- _, err := bc.stateCache.OpenTrie(hash)
- return err == nil
-}
-
-// HasBlockAndState checks if a block and associated state trie is fully present
-// in the database or not, caching it if present.
-func (bc *BlockChain) HasBlockAndState(hash common.Hash, number uint64) bool {
- // Check first that the block itself is known
- block := bc.GetBlock(hash, number)
- if block == nil {
- return false
- }
- return bc.HasState(block.Root())
-}
-
-// GetBlock retrieves a block from the database by hash and number,
-// caching it if found.
-func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
- // Short circuit if the block's already in the cache, retrieve otherwise
- if block, ok := bc.blockCache.Get(hash); ok {
- return block.(*types.Block)
- }
- block := rawdb.ReadBlock(bc.db, hash, number)
- if block == nil {
- return nil
- }
- // Cache the found block for next time and return
- bc.blockCache.Add(block.Hash(), block)
- return block
-}
-
-// GetBlockByHash retrieves a block from the database by hash, caching it if found.
-func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block {
- number := bc.hc.GetBlockNumber(hash)
- if number == nil {
- return nil
- }
- return bc.GetBlock(hash, *number)
-}
-
-// GetBlockByNumber retrieves a block from the database by number, caching it
-// (associated with its hash) if found.
-func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {
- hash := rawdb.ReadCanonicalHash(bc.db, number)
- if hash == (common.Hash{}) {
- return nil
- }
- return bc.GetBlock(hash, number)
-}
-
-// GetReceiptsByHash retrieves the receipts for all transactions in a given block.
-func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
- if receipts, ok := bc.receiptsCache.Get(hash); ok {
- return receipts.(types.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.
-// [deprecated by eth/62]
-func (bc *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) {
- number := bc.hc.GetBlockNumber(hash)
- if number == nil {
- return nil
- }
- for i := 0; i < n; i++ {
- block := bc.GetBlock(hash, *number)
- if block == nil {
- break
- }
- blocks = append(blocks, block)
- hash = block.ParentHash()
- *number--
- }
- return
-}
-
-// GetUnclesInChain retrieves all the uncles from a given block backwards until
-// a specific distance is reached.
-func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header {
- uncles := []*types.Header{}
- for i := 0; block != nil && i < length; i++ {
- uncles = append(uncles, block.Uncles()...)
- block = bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
- }
- return uncles
-}
-
-// TrieNode retrieves a blob of data associated with a trie node
-// either from ephemeral in-memory cache, or from persistent storage.
-func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) {
- return bc.stateCache.TrieDB().Node(hash)
-}
-
-// ContractCode retrieves a blob of data associated with a contract hash
-// either from ephemeral in-memory cache, or from persistent storage.
-func (bc *BlockChain) ContractCode(hash common.Hash) ([]byte, error) {
- return bc.stateCache.ContractCode(common.Hash{}, hash)
-}
-
-// ContractCodeWithPrefix retrieves a blob of data associated with a contract
-// hash either from ephemeral in-memory cache, or from persistent storage.
-//
-// If the code doesn't exist in the in-memory cache, check the storage with
-// new code scheme.
-func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) {
- type codeReader interface {
- ContractCodeWithPrefix(addrHash, codeHash common.Hash) ([]byte, error)
- }
- return bc.stateCache.(codeReader).ContractCodeWithPrefix(common.Hash{}, hash)
-}
-
// Stop stops the blockchain service. If any imports are currently in progress
// it will abort them using the procInterrupt.
func (bc *BlockChain) Stop() {
if !atomic.CompareAndSwapInt32(&bc.running, 0, 1) {
return
}
- // Unsubscribe all subscriptions registered from blockchain
+
+ // Unsubscribe all subscriptions registered from blockchain.
bc.scope.Close()
+
+ // Signal shutdown to all goroutines.
close(bc.quit)
bc.StopInsert()
+
+ // Now wait for all chain modifications to end and persistent goroutines to exit.
+ //
+ // Note: Close waits for the mutex to become available, i.e. any running chain
+ // modification will have exited when Close returns. Since we also called StopInsert,
+ // the mutex should become available quickly. It cannot be taken again after Close has
+ // returned.
+ bc.chainmu.Close()
bc.wg.Wait()
// Ensure that the entirety of the state snapshot is journalled to disk.
@@ -1006,6 +797,7 @@ func (bc *BlockChain) Stop() {
log.Error("Failed to journal state snapshot", "err", err)
}
}
+
// Ensure the state of a recent block is also stored to disk before exiting.
// We're writing three different states to catch different restart scenarios:
// - HEAD: So we don't need to reprocess any blocks in the general case
@@ -1085,38 +877,6 @@ const (
SideStatTy
)
-// truncateAncient rewinds the blockchain to the specified header and deletes all
-// data in the ancient store that exceeds the specified header.
-func (bc *BlockChain) truncateAncient(head uint64) error {
- frozen, err := bc.db.Ancients()
- if err != nil {
- return err
- }
- // Short circuit if there is no data to truncate in ancient store.
- if frozen <= head+1 {
- return nil
- }
- // Truncate all the data in the freezer beyond the specified head
- if err := bc.db.TruncateAncients(head + 1); err != nil {
- return err
- }
- // Clear out any stale content from the caches
- bc.hc.headerCache.Purge()
- bc.hc.tdCache.Purge()
- bc.hc.numberCache.Purge()
-
- // Clear out any stale content from the caches
- bc.bodyCache.Purge()
- bc.bodyRLPCache.Purge()
- bc.receiptsCache.Purge()
- bc.blockCache.Purge()
- bc.txLookupCache.Purge()
- bc.futureBlocks.Purge()
-
- log.Info("Rewind ancient data", "number", head)
- return nil
-}
-
// numberHash is just a container for a number and a hash, to represent a block
type numberHash struct {
number uint64
@@ -1155,12 +915,16 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
var (
stats = struct{ processed, ignored int32 }{}
start = time.Now()
- size = 0
+ size = int64(0)
)
+
// updateHead updates the head fast sync block if the inserted blocks are better
// and returns an indicator whether the inserted blocks are canonical.
updateHead := func(head *types.Block) bool {
- bc.chainmu.Lock()
+ if !bc.chainmu.TryLock() {
+ return false
+ }
+ defer bc.chainmu.Unlock()
// Rewind may have occurred, skip in that case.
if bc.CurrentHeader().Number.Cmp(head.Number()) >= 0 {
@@ -1169,68 +933,63 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
rawdb.WriteHeadFastBlockHash(bc.db, head.Hash())
bc.currentFastBlock.Store(head)
headFastBlockGauge.Update(int64(head.NumberU64()))
- bc.chainmu.Unlock()
return true
}
}
- bc.chainmu.Unlock()
return false
}
+
// writeAncient writes blockchain and corresponding receipt chain into ancient store.
//
// this function only accepts canonical chain data. All side chain will be reverted
// eventually.
writeAncient := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
- var (
- previous = bc.CurrentFastBlock()
- batch = bc.db.NewBatch()
- )
- // If any error occurs before updating the head or we are inserting a side chain,
- // all the data written this time wll be rolled back.
- defer func() {
- if previous != nil {
- if err := bc.truncateAncient(previous.NumberU64()); err != nil {
- log.Crit("Truncate ancient store failed", "err", err)
- }
- }
- }()
- var deleted []*numberHash
- for i, block := range blockChain {
- // Short circuit insertion if shutting down or processing failed
- if bc.insertStopped() {
- return 0, errInsertionInterrupted
- }
- // Short circuit insertion if it is required(used in testing only)
- if bc.terminateInsert != nil && bc.terminateInsert(block.Hash(), block.NumberU64()) {
- return i, errors.New("insertion is terminated for testing purpose")
- }
- // 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 block.NumberU64() == 1 {
- // Make sure to write the genesis into the freezer
- if frozen, _ := bc.db.Ancients(); frozen == 0 {
- h := rawdb.ReadCanonicalHash(bc.db, 0)
- b := rawdb.ReadBlock(bc.db, h, 0)
- size += rawdb.WriteAncientBlock(bc.db, b, rawdb.ReadReceipts(bc.db, h, 0, bc.chainConfig), rawdb.ReadTd(bc.db, h, 0))
- log.Info("Wrote genesis to ancients")
- }
- }
- // Flush data into ancient database.
- size += rawdb.WriteAncientBlock(bc.db, block, receiptChain[i], bc.GetTd(block.Hash(), block.NumberU64()))
+ first := blockChain[0]
+ last := blockChain[len(blockChain)-1]
- // Write tx indices if any condition is satisfied:
- // * If user requires to reserve all tx indices(txlookuplimit=0)
- // * If all ancient tx indices are required to be reserved(txlookuplimit is even higher than ancientlimit)
- // * If block number is large enough to be regarded as a recent block
- // It means blocks below the ancientLimit-txlookupLimit won't be indexed.
- //
- // But if the `TxIndexTail` is not nil, e.g. Geth is initialized with
- // an external ancient database, during the setup, blockchain will start
- // a background routine to re-indexed all indices in [ancients - txlookupLimit, ancients)
- // range. In this case, all tx indices of newly imported blocks should be
- // generated.
+ // Ensure genesis is in ancients.
+ if first.NumberU64() == 1 {
+ if frozen, _ := bc.db.Ancients(); frozen == 0 {
+ b := bc.genesisBlock
+ td := bc.genesisBlock.Difficulty()
+ writeSize, err := rawdb.WriteAncientBlocks(bc.db, []*types.Block{b}, []types.Receipts{nil}, td)
+ size += writeSize
+ if err != nil {
+ log.Error("Error writing genesis to ancients", "err", err)
+ return 0, err
+ }
+ log.Info("Wrote genesis to ancients")
+ }
+ }
+ // Before writing the blocks to the ancients, we need to ensure that
+ // they correspond to the what the headerchain 'expects'.
+ // We only check the last block/header, since it's a contiguous chain.
+ if !bc.HasHeader(last.Hash(), last.NumberU64()) {
+ return 0, fmt.Errorf("containing header #%d [%x..] unknown", last.Number(), last.Hash().Bytes()[:4])
+ }
+
+ // Write all chain data to ancients.
+ td := bc.GetTd(first.Hash(), first.NumberU64())
+ writeSize, err := rawdb.WriteAncientBlocks(bc.db, blockChain, receiptChain, td)
+ size += writeSize
+ if err != nil {
+ log.Error("Error importing chain data to ancients", "err", err)
+ return 0, err
+ }
+
+ // Write tx indices if any condition is satisfied:
+ // * If user requires to reserve all tx indices(txlookuplimit=0)
+ // * If all ancient tx indices are required to be reserved(txlookuplimit is even higher than ancientlimit)
+ // * If block number is large enough to be regarded as a recent block
+ // It means blocks below the ancientLimit-txlookupLimit won't be indexed.
+ //
+ // But if the `TxIndexTail` is not nil, e.g. Geth is initialized with
+ // an external ancient database, during the setup, blockchain will start
+ // a background routine to re-indexed all indices in [ancients - txlookupLimit, ancients)
+ // range. In this case, all tx indices of newly imported blocks should be
+ // generated.
+ var batch = bc.db.NewBatch()
+ for _, block := range blockChain {
if bc.txLookupLimit == 0 || ancientLimit <= bc.txLookupLimit || block.NumberU64() >= ancientLimit-bc.txLookupLimit {
rawdb.WriteTxLookupEntriesByBlock(batch, block)
} else if rawdb.ReadTxIndexTail(bc.db) != nil {
@@ -1238,51 +997,50 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
}
stats.processed++
}
+
// Flush all tx-lookup index data.
- size += batch.ValueSize()
+ size += int64(batch.ValueSize())
if err := batch.Write(); err != nil {
+ // The tx index data could not be written.
+ // Roll back the ancient store update.
+ fastBlock := bc.CurrentFastBlock().NumberU64()
+ if err := bc.db.TruncateAncients(fastBlock + 1); err != nil {
+ log.Error("Can't truncate ancient store after failed insert", "err", err)
+ }
return 0, err
}
- batch.Reset()
// Sync the ancient store explicitly to ensure all data has been flushed to disk.
if err := bc.db.Sync(); err != nil {
return 0, err
}
+
+ // Update the current fast block because all block data is now present in DB.
+ previousFastBlock := bc.CurrentFastBlock().NumberU64()
if !updateHead(blockChain[len(blockChain)-1]) {
- return 0, errors.New("side blocks can't be accepted as the ancient chain data")
- }
- previous = nil // disable rollback explicitly
-
- // Wipe out canonical block data.
- for _, nh := range deleted {
- rawdb.DeleteBlockWithoutNumber(batch, nh.hash, nh.number)
- rawdb.DeleteCanonicalHash(batch, nh.number)
- }
- for _, block := range blockChain {
- // Always keep genesis block in active database.
- if block.NumberU64() != 0 {
- rawdb.DeleteBlockWithoutNumber(batch, block.Hash(), block.NumberU64())
- rawdb.DeleteCanonicalHash(batch, block.NumberU64())
+ // We end up here if the header chain has reorg'ed, and the blocks/receipts
+ // don't match the canonical chain.
+ if err := bc.db.TruncateAncients(previousFastBlock + 1); err != nil {
+ log.Error("Can't truncate ancient store after failed insert", "err", err)
}
+ return 0, errSideChainReceipts
}
- if err := batch.Write(); err != nil {
- return 0, err
- }
+
+ // Delete block data from the main database.
batch.Reset()
-
- // Wipe out side chain too.
- for _, nh := range deleted {
- for _, hash := range rawdb.ReadAllHashes(bc.db, nh.number) {
- rawdb.DeleteBlock(batch, hash, nh.number)
- }
- }
+ canonHashes := make(map[common.Hash]struct{})
for _, block := range blockChain {
- // Always keep genesis block in active database.
- if block.NumberU64() != 0 {
- for _, hash := range rawdb.ReadAllHashes(bc.db, block.NumberU64()) {
- rawdb.DeleteBlock(batch, hash, block.NumberU64())
- }
+ canonHashes[block.Hash()] = struct{}{}
+ if block.NumberU64() == 0 {
+ continue
+ }
+ rawdb.DeleteCanonicalHash(batch, block.NumberU64())
+ rawdb.DeleteBlockWithoutNumber(batch, block.Hash(), block.NumberU64())
+ }
+ // Delete side chain hash-to-number mappings.
+ for _, nh := range rawdb.ReadAllHashesInRange(bc.db, first.NumberU64(), last.NumberU64()) {
+ if _, canon := canonHashes[nh.Hash]; !canon {
+ rawdb.DeleteHeader(batch, nh.Hash, nh.Number)
}
}
if err := batch.Write(); err != nil {
@@ -1290,6 +1048,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
}
return 0, nil
}
+
// writeLive writes blockchain and corresponding receipt chain into active store.
writeLive := func(blockChain types.Blocks, receiptChain []types.Receipts) (int, error) {
skipPresenceCheck := false
@@ -1327,7 +1086,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
if err := batch.Write(); err != nil {
return 0, err
}
- size += batch.ValueSize()
+ size += int64(batch.ValueSize())
batch.Reset()
}
stats.processed++
@@ -1336,7 +1095,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
// we can ensure all components of body is completed(body, receipts,
// tx indexes)
if batch.ValueSize() > 0 {
- size += batch.ValueSize()
+ size += int64(batch.ValueSize())
if err := batch.Write(); err != nil {
return 0, err
}
@@ -1344,6 +1103,7 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
updateHead(blockChain[len(blockChain)-1])
return 0, nil
}
+
// Write downloaded chain data and corresponding receipt chain data
if len(ancientBlocks) > 0 {
if n, err := writeAncient(ancientBlocks, ancientReceipts); err != nil {
@@ -1389,26 +1149,15 @@ func (bc *BlockChain) InsertReceiptChain(blockChain types.Blocks, receiptChain [
return 0, nil
}
-// SetTxLookupLimit is responsible for updating the txlookup limit to the
-// original one stored in db if the new mismatches with the old one.
-func (bc *BlockChain) SetTxLookupLimit(limit uint64) {
- bc.txLookupLimit = limit
-}
-
-// TxLookupLimit retrieves the txlookup limit used by blockchain to prune
-// stale transaction indices.
-func (bc *BlockChain) TxLookupLimit() uint64 {
- return bc.txLookupLimit
-}
-
var lastWrite uint64
// writeBlockWithoutState writes only the block and its metadata to the database,
// but does not write any state. This is used to construct competing side forks
// up to the point where they exceed the canonical total difficulty.
func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (err error) {
- bc.wg.Add(1)
- defer bc.wg.Done()
+ if bc.insertStopped() {
+ return errInsertionInterrupted
+ }
batch := bc.db.NewBatch()
rawdb.WriteTd(batch, block.Hash(), block.NumberU64(), td)
@@ -1422,9 +1171,6 @@ func (bc *BlockChain) writeBlockWithoutState(block *types.Block, td *big.Int) (e
// writeKnownBlock updates the head block flag with a known block
// and introduces chain reorg if necessary.
func (bc *BlockChain) writeKnownBlock(block *types.Block) error {
- bc.wg.Add(1)
- defer bc.wg.Done()
-
current := bc.CurrentBlock()
if block.ParentHash() != current.Hash() {
if err := bc.reorg(current, block); err != nil {
@@ -1437,17 +1183,19 @@ func (bc *BlockChain) writeKnownBlock(block *types.Block) error {
// WriteBlockWithState writes the block and all associated state to the database.
func (bc *BlockChain) WriteBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
- bc.chainmu.Lock()
+ if !bc.chainmu.TryLock() {
+ return NonStatTy, errInsertionInterrupted
+ }
defer bc.chainmu.Unlock()
-
return bc.writeBlockWithState(block, receipts, logs, state, emitHeadEvent)
}
// writeBlockWithState writes the block and all associated state to the database,
// but is expects the chain mutex to be held.
func (bc *BlockChain) writeBlockWithState(block *types.Block, receipts []*types.Receipt, logs []*types.Log, state *state.StateDB, emitHeadEvent bool) (status WriteStatus, err error) {
- bc.wg.Add(1)
- defer bc.wg.Done()
+ if bc.insertStopped() {
+ return NonStatTy, errInsertionInterrupted
+ }
// Calculate the total difficulty of the block
ptd := bc.GetTd(block.ParentHash(), block.NumberU64()-1)
@@ -1611,31 +1359,28 @@ func (bc *BlockChain) InsertChain(chain types.Blocks) (int, error) {
bc.blockProcFeed.Send(true)
defer bc.blockProcFeed.Send(false)
- // Remove already known canon-blocks
- var (
- block, prev *types.Block
- )
- // Do a sanity check that the provided chain is actually ordered and linked
+ // Do a sanity check that the provided chain is actually ordered and linked.
for i := 1; i < len(chain); i++ {
- block = chain[i]
- prev = chain[i-1]
+ block, prev := chain[i], chain[i-1]
if block.NumberU64() != prev.NumberU64()+1 || block.ParentHash() != prev.Hash() {
- // Chain broke ancestry, log a message (programming error) and skip insertion
- log.Error("Non contiguous block insert", "number", block.Number(), "hash", block.Hash(),
- "parent", block.ParentHash(), "prevnumber", prev.Number(), "prevhash", prev.Hash())
-
+ log.Error("Non contiguous block insert",
+ "number", block.Number(),
+ "hash", block.Hash(),
+ "parent", block.ParentHash(),
+ "prevnumber", prev.Number(),
+ "prevhash", prev.Hash(),
+ )
return 0, fmt.Errorf("non contiguous insert: item %d is #%d [%x..], item %d is #%d [%x..] (parent [%x..])", i-1, prev.NumberU64(),
prev.Hash().Bytes()[:4], i, block.NumberU64(), block.Hash().Bytes()[:4], block.ParentHash().Bytes()[:4])
}
}
- // Pre-checks passed, start the full block imports
- bc.wg.Add(1)
- bc.chainmu.Lock()
- n, err := bc.insertChain(chain, true)
- bc.chainmu.Unlock()
- bc.wg.Done()
- return n, err
+ // Pre-check passed, start the full block imports.
+ if !bc.chainmu.TryLock() {
+ return 0, errChainStopped
+ }
+ defer bc.chainmu.Unlock()
+ return bc.insertChain(chain, true)
}
// InsertChainWithoutSealVerification works exactly the same
@@ -1644,14 +1389,11 @@ func (bc *BlockChain) InsertChainWithoutSealVerification(block *types.Block) (in
bc.blockProcFeed.Send(true)
defer bc.blockProcFeed.Send(false)
- // Pre-checks passed, start the full block imports
- bc.wg.Add(1)
- bc.chainmu.Lock()
- n, err := bc.insertChain(types.Blocks([]*types.Block{block}), false)
- bc.chainmu.Unlock()
- bc.wg.Done()
-
- return n, err
+ if !bc.chainmu.TryLock() {
+ return 0, errChainStopped
+ }
+ defer bc.chainmu.Unlock()
+ return bc.insertChain(types.Blocks([]*types.Block{block}), false)
}
// insertChain is the internal implementation of InsertChain, which assumes that
@@ -1663,10 +1405,11 @@ func (bc *BlockChain) InsertChainWithoutSealVerification(block *types.Block) (in
// is imported, but then new canon-head is added before the actual sidechain
// completes, then the historic state could be pruned again
func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, error) {
- // If the chain is terminating, don't even bother starting up
- if atomic.LoadInt32(&bc.procInterrupt) == 1 {
+ // If the chain is terminating, don't even bother starting up.
+ if bc.insertStopped() {
return 0, nil
}
+
// Start a parallel signature recovery (signer will fluke on fork transition, minimal perf loss)
senderCacher.recoverFromBlocks(types.MakeSigner(bc.chainConfig, chain[0].Number()), chain)
@@ -1693,22 +1436,21 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
// Peek the error for the first block to decide the directing import logic
it := newInsertIterator(chain, results, bc.validator)
-
block, err := it.next()
- // Left-trim all the known blocks
- if err == ErrKnownBlock {
+ // Left-trim all the known blocks that don't need to build snapshot
+ if bc.skipBlock(err, it) {
// First block (and state) is known
// 1. We did a roll-back, and should now do a re-import
// 2. The block is stored as a sidechain, and is lying about it's stateroot, and passes a stateroot
- // from the canonical chain, which has not been verified.
- // Skip all known blocks that are behind us
+ // from the canonical chain, which has not been verified.
+ // Skip all known blocks that are behind us.
var (
current = bc.CurrentBlock()
localTd = bc.GetTd(current.Hash(), current.NumberU64())
externTd = bc.GetTd(block.ParentHash(), block.NumberU64()-1) // The first block can't be nil
)
- for block != nil && err == ErrKnownBlock {
+ for block != nil && bc.skipBlock(err, it) {
externTd = new(big.Int).Add(externTd, block.Difficulty())
if localTd.Cmp(externTd) < 0 {
break
@@ -1726,7 +1468,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
// When node runs a fast sync again, it can re-import a batch of known blocks via
// `insertChain` while a part of them have higher total difficulty than current
// head full block(new pivot point).
- for block != nil && err == ErrKnownBlock {
+ for block != nil && bc.skipBlock(err, it) {
log.Debug("Writing previously known block", "number", block.Number(), "hash", block.Hash())
if err := bc.writeKnownBlock(block); err != nil {
return it.index, err
@@ -1758,8 +1500,10 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
// If there are any still remaining, mark as ignored
return it.index, err
- // Some other error occurred, abort
- case err != nil:
+ // Some other error(except ErrKnownBlock) occurred, abort.
+ // ErrKnownBlock is allowed here since some known blocks
+ // still need re-execution to generate snapshots that are missing
+ case err != nil && !errors.Is(err, ErrKnownBlock):
bc.futureBlocks.Remove(block.Hash())
stats.ignored += len(it.chain)
bc.reportBlock(block, nil, err)
@@ -1777,7 +1521,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
}
}()
- for ; block != nil && err == nil || err == ErrKnownBlock; block, err = it.next() {
+ for ; block != nil && err == nil || errors.Is(err, ErrKnownBlock); block, err = it.next() {
// If the chain is terminating, stop processing blocks
if bc.insertStopped() {
log.Debug("Abort during block processing")
@@ -1792,8 +1536,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
// Clique blocks where they can share state among each other, so importing an
// older block might complete the state of the subsequent one. In this case,
// just skip the block (we already validated it once fully (and crashed), since
- // its header and body was already in the database).
- if err == ErrKnownBlock {
+ // its header and body was already in the database). But if the corresponding
+ // snapshot layer is missing, forcibly rerun the execution to build it.
+ if bc.skipBlock(err, it) {
logger := log.Debug
if bc.chainConfig.Clique == nil {
logger = log.Warn
@@ -1826,9 +1571,9 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
lastCanon = block
continue
}
+
// Retrieve the parent block and it's state to execute on top
start := time.Now()
-
parent := it.previous()
if parent == nil {
parent = bc.GetHeader(block.ParentHash(), block.NumberU64()-1)
@@ -1837,6 +1582,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
if err != nil {
return it.index, err
}
+
// Enable prefetching to pull in trie node paths while processing transactions
statedb.StartPrefetcher("chain")
activeState = statedb
@@ -1858,6 +1604,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
}(time.Now(), followup, throwaway, &followupInterrupt)
}
}
+
// Process block using the parent state as reference point
substart := time.Now()
receipts, logs, usedGas, err := bc.processor.Process(block, statedb, bc.vmConfig)
@@ -1866,6 +1613,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
atomic.StoreUint32(&followupInterrupt, 1)
return it.index, err
}
+
// Update the metrics touched during block processing
accountReadTimer.Update(statedb.AccountReads) // Account reads are complete, we can mark them
storageReadTimer.Update(statedb.StorageReads) // Storage reads are complete, we can mark them
@@ -1941,6 +1689,7 @@ func (bc *BlockChain) insertChain(chain types.Blocks, verifySeals bool) (int, er
dirty, _ := bc.stateCache.TrieDB().Size()
stats.report(chain, it.index, dirty)
}
+
// Any blocks remaining here? The only ones we care about are the future ones
if block != nil && errors.Is(err, consensus.ErrFutureBlock) {
if err := bc.addFutureBlock(block); err != nil {
@@ -2250,7 +1999,10 @@ func (bc *BlockChain) reorg(oldBlock, newBlock *types.Block) error {
return nil
}
-func (bc *BlockChain) update() {
+// futureBlocksLoop processes the 'future block' queue.
+func (bc *BlockChain) futureBlocksLoop() {
+ defer bc.wg.Done()
+
futureTimer := time.NewTicker(5 * time.Second)
defer futureTimer.Stop()
for {
@@ -2263,6 +2015,47 @@ func (bc *BlockChain) update() {
}
}
+// skipBlock returns 'true', if the block being imported can be skipped over, meaning
+// that the block does not need to be processed but can be considered already fully 'done'.
+func (bc *BlockChain) skipBlock(err error, it *insertIterator) bool {
+ // We can only ever bypass processing if the only error returned by the validator
+ // is ErrKnownBlock, which means all checks passed, but we already have the block
+ // and state.
+ if !errors.Is(err, ErrKnownBlock) {
+ return false
+ }
+ // If we're not using snapshots, we can skip this, since we have both block
+ // and (trie-) state
+ if bc.snaps == nil {
+ return true
+ }
+ var (
+ header = it.current() // header can't be nil
+ parentRoot common.Hash
+ )
+ // If we also have the snapshot-state, we can skip the processing.
+ if bc.snaps.Snapshot(header.Root) != nil {
+ return true
+ }
+ // In this case, we have the trie-state but not snapshot-state. If the parent
+ // snapshot-state exists, we need to process this in order to not get a gap
+ // in the snapshot layers.
+ // Resolve parent block
+ if parent := it.previous(); parent != nil {
+ parentRoot = parent.Root
+ } else if parent = bc.GetHeaderByHash(header.ParentHash); parent != nil {
+ parentRoot = parent.Root
+ }
+ if parentRoot == (common.Hash{}) {
+ return false // Theoretically impossible case
+ }
+ // Parent is also missing snapshot: we can skip this. Otherwise process.
+ if bc.snaps.Snapshot(parentRoot) == nil {
+ return true
+ }
+ return false
+}
+
// maintainTxIndex is responsible for the construction and deletion of the
// transaction index.
//
@@ -2287,6 +2080,7 @@ func (bc *BlockChain) maintainTxIndex(ancients uint64) {
}
rawdb.IndexTransactions(bc.db, from, ancients, bc.quit)
}
+
// indexBlocks reindexes or unindexes transactions depending on user configuration
indexBlocks := func(tail *uint64, head uint64, done chan struct{}) {
defer func() { done <- struct{}{} }()
@@ -2319,6 +2113,7 @@ func (bc *BlockChain) maintainTxIndex(ancients uint64) {
rawdb.UnindexTransactions(bc.db, *tail, head-bc.txLookupLimit+1, bc.quit)
}
}
+
// Any reindexing done, start listening to chain events and moving the index window
var (
done chan struct{} // Non-nil if background unindexing or reindexing routine is active.
@@ -2386,137 +2181,10 @@ func (bc *BlockChain) InsertHeaderChain(chain []*types.Header, checkFreq int) (i
return i, err
}
- // Make sure only one thread manipulates the chain at once
- bc.chainmu.Lock()
+ if !bc.chainmu.TryLock() {
+ return 0, errChainStopped
+ }
defer bc.chainmu.Unlock()
-
- bc.wg.Add(1)
- defer bc.wg.Done()
_, err := bc.hc.InsertHeaderChain(chain, start)
return 0, 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 {
- return bc.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 (bc *BlockChain) GetTd(hash common.Hash, number uint64) *big.Int {
- return bc.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 (bc *BlockChain) GetTdByHash(hash common.Hash) *big.Int {
- return bc.hc.GetTdByHash(hash)
-}
-
-// GetHeader retrieves a block header from the database by hash and number,
-// caching it if found.
-func (bc *BlockChain) GetHeader(hash common.Hash, number uint64) *types.Header {
- // Blockchain might have cached the whole block, only if not go to headerchain
- if block, ok := bc.blockCache.Get(hash); ok {
- return block.(*types.Block).Header()
- }
-
- return bc.hc.GetHeader(hash, number)
-}
-
-// GetHeaderByHash retrieves a block header from the database by hash, caching it if
-// found.
-func (bc *BlockChain) GetHeaderByHash(hash common.Hash) *types.Header {
- // Blockchain might have cached the whole block, only if not go to headerchain
- if block, ok := bc.blockCache.Get(hash); ok {
- return block.(*types.Block).Header()
- }
-
- return bc.hc.GetHeaderByHash(hash)
-}
-
-// HasHeader checks if a block header is present in the database or not, caching
-// it if present.
-func (bc *BlockChain) HasHeader(hash common.Hash, number uint64) bool {
- return bc.hc.HasHeader(hash, number)
-}
-
-// GetCanonicalHash returns the canonical hash for a given block number
-func (bc *BlockChain) GetCanonicalHash(number uint64) common.Hash {
- return bc.hc.GetCanonicalHash(number)
-}
-
-// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
-// hash, fetching towards the genesis block.
-func (bc *BlockChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
- return bc.hc.GetBlockHashesFromHash(hash, max)
-}
-
-// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
-// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
-// number of blocks to be individually checked before we reach the canonical chain.
-//
-// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
-func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
- return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
-}
-
-// GetHeaderByNumber retrieves a block header from the database by number,
-// caching it (associated with its hash) if found.
-func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
- return bc.hc.GetHeaderByNumber(number)
-}
-
-// GetTransactionLookup retrieves the lookup associate with the given transaction
-// hash from the cache or database.
-func (bc *BlockChain) GetTransactionLookup(hash common.Hash) *rawdb.LegacyTxLookupEntry {
- // Short circuit if the txlookup already in the cache, retrieve otherwise
- if lookup, exist := bc.txLookupCache.Get(hash); exist {
- return lookup.(*rawdb.LegacyTxLookupEntry)
- }
- tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
- if tx == nil {
- return nil
- }
- lookup := &rawdb.LegacyTxLookupEntry{BlockHash: blockHash, BlockIndex: blockNumber, Index: txIndex}
- bc.txLookupCache.Add(hash, lookup)
- return lookup
-}
-
-// Config retrieves the chain's fork configuration.
-func (bc *BlockChain) Config() *params.ChainConfig { return bc.chainConfig }
-
-// Engine retrieves the blockchain's consensus engine.
-func (bc *BlockChain) Engine() consensus.Engine { return bc.engine }
-
-// SubscribeRemovedLogsEvent registers a subscription of RemovedLogsEvent.
-func (bc *BlockChain) SubscribeRemovedLogsEvent(ch chan<- RemovedLogsEvent) event.Subscription {
- return bc.scope.Track(bc.rmLogsFeed.Subscribe(ch))
-}
-
-// SubscribeChainEvent registers a subscription of ChainEvent.
-func (bc *BlockChain) SubscribeChainEvent(ch chan<- ChainEvent) event.Subscription {
- return bc.scope.Track(bc.chainFeed.Subscribe(ch))
-}
-
-// SubscribeChainHeadEvent registers a subscription of ChainHeadEvent.
-func (bc *BlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription {
- return bc.scope.Track(bc.chainHeadFeed.Subscribe(ch))
-}
-
-// SubscribeChainSideEvent registers a subscription of ChainSideEvent.
-func (bc *BlockChain) SubscribeChainSideEvent(ch chan<- ChainSideEvent) event.Subscription {
- return bc.scope.Track(bc.chainSideFeed.Subscribe(ch))
-}
-
-// SubscribeLogsEvent registers a subscription of []*types.Log.
-func (bc *BlockChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
- return bc.scope.Track(bc.logsFeed.Subscribe(ch))
-}
-
-// SubscribeBlockProcessingEvent registers a subscription of bool where true means
-// block processing has started while false means it has stopped.
-func (bc *BlockChain) SubscribeBlockProcessingEvent(ch chan<- bool) event.Subscription {
- return bc.scope.Track(bc.blockProcFeed.Subscribe(ch))
-}
diff --git a/core/blockchain_insert.go b/core/blockchain_insert.go
index cb8473c084..4464870279 100644
--- a/core/blockchain_insert.go
+++ b/core/blockchain_insert.go
@@ -150,6 +150,14 @@ func (it *insertIterator) previous() *types.Header {
return it.chain[it.index-1].Header()
}
+// current returns the current header that is being processed, or nil.
+func (it *insertIterator) current() *types.Header {
+ if it.index == -1 || it.index >= len(it.chain) {
+ return nil
+ }
+ return it.chain[it.index].Header()
+}
+
// first returns the first block in the it.
func (it *insertIterator) first() *types.Block {
return it.chain[0]
diff --git a/core/blockchain_reader.go b/core/blockchain_reader.go
new file mode 100644
index 0000000000..beaa57b0c1
--- /dev/null
+++ b/core/blockchain_reader.go
@@ -0,0 +1,387 @@
+// 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 core
+
+import (
+ "math/big"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/state/snapshot"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/rlp"
+)
+
+// 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 {
+ return bc.hc.CurrentHeader()
+}
+
+// CurrentBlock retrieves the current head block of the canonical chain. The
+// block is retrieved from the blockchain's internal cache.
+func (bc *BlockChain) CurrentBlock() *types.Block {
+ return bc.currentBlock.Load().(*types.Block)
+}
+
+// CurrentFastBlock retrieves the current fast-sync head block of the canonical
+// chain. The block is retrieved from the blockchain's internal cache.
+func (bc *BlockChain) CurrentFastBlock() *types.Block {
+ return bc.currentFastBlock.Load().(*types.Block)
+}
+
+// HasHeader checks if a block header is present in the database or not, caching
+// it if present.
+func (bc *BlockChain) HasHeader(hash common.Hash, number uint64) bool {
+ return bc.hc.HasHeader(hash, number)
+}
+
+// GetHeader retrieves a block header from the database by hash and number,
+// caching it if found.
+func (bc *BlockChain) GetHeader(hash common.Hash, number uint64) *types.Header {
+ return bc.hc.GetHeader(hash, number)
+}
+
+// GetHeaderByHash retrieves a block header from the database by hash, caching it if
+// found.
+func (bc *BlockChain) GetHeaderByHash(hash common.Hash) *types.Header {
+ return bc.hc.GetHeaderByHash(hash)
+}
+
+// GetHeaderByNumber retrieves a block header from the database by number,
+// caching it (associated with its hash) if found.
+func (bc *BlockChain) GetHeaderByNumber(number uint64) *types.Header {
+ return bc.hc.GetHeaderByNumber(number)
+}
+
+// GetBody retrieves a block body (transactions and uncles) from the database by
+// hash, caching it if found.
+func (bc *BlockChain) GetBody(hash common.Hash) *types.Body {
+ // Short circuit if the body's already in the cache, retrieve otherwise
+ if cached, ok := bc.bodyCache.Get(hash); ok {
+ body := cached.(*types.Body)
+ return body
+ }
+ number := bc.hc.GetBlockNumber(hash)
+ if number == nil {
+ return nil
+ }
+ body := rawdb.ReadBody(bc.db, hash, *number)
+ if body == nil {
+ return nil
+ }
+ // Cache the found body for next time and return
+ bc.bodyCache.Add(hash, body)
+ return body
+}
+
+// GetBodyRLP retrieves a block body in RLP encoding from the database by hash,
+// caching it if found.
+func (bc *BlockChain) GetBodyRLP(hash common.Hash) rlp.RawValue {
+ // Short circuit if the body's already in the cache, retrieve otherwise
+ if cached, ok := bc.bodyRLPCache.Get(hash); ok {
+ return cached.(rlp.RawValue)
+ }
+ number := bc.hc.GetBlockNumber(hash)
+ if number == nil {
+ return nil
+ }
+ body := rawdb.ReadBodyRLP(bc.db, hash, *number)
+ if len(body) == 0 {
+ return nil
+ }
+ // Cache the found body for next time and return
+ bc.bodyRLPCache.Add(hash, body)
+ return body
+}
+
+// HasBlock checks if a block is fully present in the database or not.
+func (bc *BlockChain) HasBlock(hash common.Hash, number uint64) bool {
+ if bc.blockCache.Contains(hash) {
+ return true
+ }
+ return rawdb.HasBody(bc.db, hash, number)
+}
+
+// HasFastBlock checks if a fast block is fully present in the database or not.
+func (bc *BlockChain) HasFastBlock(hash common.Hash, number uint64) bool {
+ if !bc.HasBlock(hash, number) {
+ return false
+ }
+ if bc.receiptsCache.Contains(hash) {
+ return true
+ }
+ return rawdb.HasReceipts(bc.db, hash, number)
+}
+
+// GetBlock retrieves a block from the database by hash and number,
+// caching it if found.
+func (bc *BlockChain) GetBlock(hash common.Hash, number uint64) *types.Block {
+ // Short circuit if the block's already in the cache, retrieve otherwise
+ if block, ok := bc.blockCache.Get(hash); ok {
+ return block.(*types.Block)
+ }
+ block := rawdb.ReadBlock(bc.db, hash, number)
+ if block == nil {
+ return nil
+ }
+ // Cache the found block for next time and return
+ bc.blockCache.Add(block.Hash(), block)
+ return block
+}
+
+// GetBlockByHash retrieves a block from the database by hash, caching it if found.
+func (bc *BlockChain) GetBlockByHash(hash common.Hash) *types.Block {
+ number := bc.hc.GetBlockNumber(hash)
+ if number == nil {
+ return nil
+ }
+ return bc.GetBlock(hash, *number)
+}
+
+// GetBlockByNumber retrieves a block from the database by number, caching it
+// (associated with its hash) if found.
+func (bc *BlockChain) GetBlockByNumber(number uint64) *types.Block {
+ hash := rawdb.ReadCanonicalHash(bc.db, number)
+ if hash == (common.Hash{}) {
+ return nil
+ }
+ return bc.GetBlock(hash, number)
+}
+
+// GetBlocksFromHash returns the block corresponding to hash and up to n-1 ancestors.
+// [deprecated by eth/62]
+func (bc *BlockChain) GetBlocksFromHash(hash common.Hash, n int) (blocks []*types.Block) {
+ number := bc.hc.GetBlockNumber(hash)
+ if number == nil {
+ return nil
+ }
+ for i := 0; i < n; i++ {
+ block := bc.GetBlock(hash, *number)
+ if block == nil {
+ break
+ }
+ blocks = append(blocks, block)
+ hash = block.ParentHash()
+ *number--
+ }
+ return
+}
+
+// GetReceiptsByHash retrieves the receipts for all transactions in a given block.
+func (bc *BlockChain) GetReceiptsByHash(hash common.Hash) types.Receipts {
+ if receipts, ok := bc.receiptsCache.Get(hash); ok {
+ return receipts.(types.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
+}
+
+// GetUnclesInChain retrieves all the uncles from a given block backwards until
+// a specific distance is reached.
+func (bc *BlockChain) GetUnclesInChain(block *types.Block, length int) []*types.Header {
+ uncles := []*types.Header{}
+ for i := 0; block != nil && i < length; i++ {
+ uncles = append(uncles, block.Uncles()...)
+ block = bc.GetBlock(block.ParentHash(), block.NumberU64()-1)
+ }
+ return uncles
+}
+
+// GetCanonicalHash returns the canonical hash for a given block number
+func (bc *BlockChain) GetCanonicalHash(number uint64) common.Hash {
+ return bc.hc.GetCanonicalHash(number)
+}
+
+// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
+// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
+// number of blocks to be individually checked before we reach the canonical chain.
+//
+// Note: ancestor == 0 returns the same block, 1 returns its parent and so on.
+func (bc *BlockChain) GetAncestor(hash common.Hash, number, ancestor uint64, maxNonCanonical *uint64) (common.Hash, uint64) {
+ return bc.hc.GetAncestor(hash, number, ancestor, maxNonCanonical)
+}
+
+// GetTransactionLookup retrieves the lookup associate with the given transaction
+// hash from the cache or database.
+func (bc *BlockChain) GetTransactionLookup(hash common.Hash) *rawdb.LegacyTxLookupEntry {
+ // Short circuit if the txlookup already in the cache, retrieve otherwise
+ if lookup, exist := bc.txLookupCache.Get(hash); exist {
+ return lookup.(*rawdb.LegacyTxLookupEntry)
+ }
+ tx, blockHash, blockNumber, txIndex := rawdb.ReadTransaction(bc.db, hash)
+ if tx == nil {
+ return nil
+ }
+ lookup := &rawdb.LegacyTxLookupEntry{BlockHash: blockHash, BlockIndex: blockNumber, Index: txIndex}
+ bc.txLookupCache.Add(hash, lookup)
+ return lookup
+}
+
+// GetTd retrieves a block's total difficulty in the canonical chain from the
+// database by hash and number, caching it if found.
+func (bc *BlockChain) GetTd(hash common.Hash, number uint64) *big.Int {
+ return bc.hc.GetTd(hash, number)
+}
+
+// HasState checks if state trie is fully present in the database or not.
+func (bc *BlockChain) HasState(hash common.Hash) bool {
+ _, err := bc.stateCache.OpenTrie(hash)
+ return err == nil
+}
+
+// HasBlockAndState checks if a block and associated state trie is fully present
+// in the database or not, caching it if present.
+func (bc *BlockChain) HasBlockAndState(hash common.Hash, number uint64) bool {
+ // Check first that the block itself is known
+ block := bc.GetBlock(hash, number)
+ if block == nil {
+ return false
+ }
+ return bc.HasState(block.Root())
+}
+
+// TrieNode retrieves a blob of data associated with a trie node
+// either from ephemeral in-memory cache, or from persistent storage.
+func (bc *BlockChain) TrieNode(hash common.Hash) ([]byte, error) {
+ return bc.stateCache.TrieDB().Node(hash)
+}
+
+// ContractCode retrieves a blob of data associated with a contract hash
+// either from ephemeral in-memory cache, or from persistent storage.
+func (bc *BlockChain) ContractCode(hash common.Hash) ([]byte, error) {
+ return bc.stateCache.ContractCode(common.Hash{}, hash)
+}
+
+// ContractCodeWithPrefix retrieves a blob of data associated with a contract
+// hash either from ephemeral in-memory cache, or from persistent storage.
+//
+// If the code doesn't exist in the in-memory cache, check the storage with
+// new code scheme.
+func (bc *BlockChain) ContractCodeWithPrefix(hash common.Hash) ([]byte, error) {
+ type codeReader interface {
+ ContractCodeWithPrefix(addrHash, codeHash common.Hash) ([]byte, error)
+ }
+ return bc.stateCache.(codeReader).ContractCodeWithPrefix(common.Hash{}, hash)
+}
+
+// State returns a new mutable state based on the current HEAD block.
+func (bc *BlockChain) State() (*state.StateDB, error) {
+ return bc.StateAt(bc.CurrentBlock().Root())
+}
+
+// StateAt returns a new mutable state based on a particular point in time.
+func (bc *BlockChain) StateAt(root common.Hash) (*state.StateDB, error) {
+ return state.New(root, bc.stateCache, bc.snaps)
+}
+
+// Config retrieves the chain's fork configuration.
+func (bc *BlockChain) Config() *params.ChainConfig { return bc.chainConfig }
+
+// Engine retrieves the blockchain's consensus engine.
+func (bc *BlockChain) Engine() consensus.Engine { return bc.engine }
+
+// Snapshots returns the blockchain snapshot tree.
+func (bc *BlockChain) Snapshots() *snapshot.Tree {
+ return bc.snaps
+}
+
+// Validator returns the current validator.
+func (bc *BlockChain) Validator() Validator {
+ return bc.validator
+}
+
+// Processor returns the current processor.
+func (bc *BlockChain) Processor() Processor {
+ return bc.processor
+}
+
+// StateCache returns the caching database underpinning the blockchain instance.
+func (bc *BlockChain) StateCache() state.Database {
+ return bc.stateCache
+}
+
+// GasLimit returns the gas limit of the current HEAD block.
+func (bc *BlockChain) GasLimit() uint64 {
+ return bc.CurrentBlock().GasLimit()
+}
+
+// Genesis retrieves the chain's genesis block.
+func (bc *BlockChain) Genesis() *types.Block {
+ return bc.genesisBlock
+}
+
+// GetVMConfig returns the block chain VM config.
+func (bc *BlockChain) GetVMConfig() *vm.Config {
+ return &bc.vmConfig
+}
+
+// SetTxLookupLimit is responsible for updating the txlookup limit to the
+// original one stored in db if the new mismatches with the old one.
+func (bc *BlockChain) SetTxLookupLimit(limit uint64) {
+ bc.txLookupLimit = limit
+}
+
+// TxLookupLimit retrieves the txlookup limit used by blockchain to prune
+// stale transaction indices.
+func (bc *BlockChain) TxLookupLimit() uint64 {
+ return bc.txLookupLimit
+}
+
+// SubscribeRemovedLogsEvent registers a subscription of RemovedLogsEvent.
+func (bc *BlockChain) SubscribeRemovedLogsEvent(ch chan<- RemovedLogsEvent) event.Subscription {
+ return bc.scope.Track(bc.rmLogsFeed.Subscribe(ch))
+}
+
+// SubscribeChainEvent registers a subscription of ChainEvent.
+func (bc *BlockChain) SubscribeChainEvent(ch chan<- ChainEvent) event.Subscription {
+ return bc.scope.Track(bc.chainFeed.Subscribe(ch))
+}
+
+// SubscribeChainHeadEvent registers a subscription of ChainHeadEvent.
+func (bc *BlockChain) SubscribeChainHeadEvent(ch chan<- ChainHeadEvent) event.Subscription {
+ return bc.scope.Track(bc.chainHeadFeed.Subscribe(ch))
+}
+
+// SubscribeChainSideEvent registers a subscription of ChainSideEvent.
+func (bc *BlockChain) SubscribeChainSideEvent(ch chan<- ChainSideEvent) event.Subscription {
+ return bc.scope.Track(bc.chainSideFeed.Subscribe(ch))
+}
+
+// SubscribeLogsEvent registers a subscription of []*types.Log.
+func (bc *BlockChain) SubscribeLogsEvent(ch chan<- []*types.Log) event.Subscription {
+ return bc.scope.Track(bc.logsFeed.Subscribe(ch))
+}
+
+// SubscribeBlockProcessingEvent registers a subscription of bool where true means
+// block processing has started while false means it has stopped.
+func (bc *BlockChain) SubscribeBlockProcessingEvent(ch chan<- bool) event.Subscription {
+ return bc.scope.Track(bc.blockProcFeed.Subscribe(ch))
+}
diff --git a/core/blockchain_repair_test.go b/core/blockchain_repair_test.go
index aca5546e20..f4f7620787 100644
--- a/core/blockchain_repair_test.go
+++ b/core/blockchain_repair_test.go
@@ -1863,3 +1863,124 @@ func testRepair(t *testing.T, tt *rewindTest, snapshots bool) {
t.Errorf("Frozen block count mismatch: have %d, want %d", frozen, tt.expFrozen)
}
}
+
+// TestIssue23496 tests scenario described in https://github.com/ethereum/go-ethereum/pull/23496#issuecomment-926393893
+// Credits to @zzyalbert for finding the issue.
+//
+// Local chain owns these blocks:
+// G B1 B2 B3 B4
+// B1: state committed
+// B2: snapshot disk layer
+// B3: state committed
+// B4: head block
+//
+// Crash happens without fully persisting snapshot and in-memory states,
+// chain rewinds itself to the B1 (skip B3 in order to recover snapshot)
+// In this case the snapshot layer of B3 is not created because of existent
+// state.
+func TestIssue23496(t *testing.T) {
+ // It's hard to follow the test case, visualize the input
+ //log.Root().SetHandler(log.LvlFilterHandler(log.LvlTrace, log.StreamHandler(os.Stderr, log.TerminalFormat(true))))
+
+ // Create a temporary persistent database
+ datadir, err := ioutil.TempDir("", "")
+ if err != nil {
+ t.Fatalf("Failed to create temporary datadir: %v", err)
+ }
+ os.RemoveAll(datadir)
+
+ db, err := rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "", false)
+ if err != nil {
+ t.Fatalf("Failed to create persistent database: %v", err)
+ }
+ defer db.Close() // Might double close, should be fine
+
+ // Initialize a fresh chain
+ var (
+ genesis = (&Genesis{BaseFee: big.NewInt(params.InitialBaseFee)}).MustCommit(db)
+ engine = ethash.NewFullFaker()
+ config = &CacheConfig{
+ TrieCleanLimit: 256,
+ TrieDirtyLimit: 256,
+ TrieTimeLimit: 5 * time.Minute,
+ SnapshotLimit: 256,
+ SnapshotWait: true,
+ }
+ )
+ chain, err := NewBlockChain(db, config, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
+ if err != nil {
+ t.Fatalf("Failed to create chain: %v", err)
+ }
+ blocks, _ := GenerateChain(params.TestChainConfig, genesis, engine, rawdb.NewMemoryDatabase(), 4, func(i int, b *BlockGen) {
+ b.SetCoinbase(common.Address{0x02})
+ b.SetDifficulty(big.NewInt(1000000))
+ })
+
+ // Insert block B1 and commit the state into disk
+ if _, err := chain.InsertChain(blocks[:1]); err != nil {
+ t.Fatalf("Failed to import canonical chain start: %v", err)
+ }
+ chain.stateCache.TrieDB().Commit(blocks[0].Root(), true, nil)
+
+ // Insert block B2 and commit the snapshot into disk
+ if _, err := chain.InsertChain(blocks[1:2]); err != nil {
+ t.Fatalf("Failed to import canonical chain start: %v", err)
+ }
+ if err := chain.snaps.Cap(blocks[1].Root(), 0); err != nil {
+ t.Fatalf("Failed to flatten snapshots: %v", err)
+ }
+
+ // Insert block B3 and commit the state into disk
+ if _, err := chain.InsertChain(blocks[2:3]); err != nil {
+ t.Fatalf("Failed to import canonical chain start: %v", err)
+ }
+ chain.stateCache.TrieDB().Commit(blocks[2].Root(), true, nil)
+
+ // Insert the remaining blocks
+ if _, err := chain.InsertChain(blocks[3:]); err != nil {
+ t.Fatalf("Failed to import canonical chain tail: %v", err)
+ }
+
+ // Pull the plug on the database, simulating a hard crash
+ db.Close()
+
+ // Start a new blockchain back up and see where the repair leads us
+ db, err = rawdb.NewLevelDBDatabaseWithFreezer(datadir, 0, 0, datadir, "", false)
+ if err != nil {
+ t.Fatalf("Failed to reopen persistent database: %v", err)
+ }
+ defer db.Close()
+
+ chain, err = NewBlockChain(db, nil, params.AllEthashProtocolChanges, engine, vm.Config{}, nil, nil)
+ if err != nil {
+ t.Fatalf("Failed to recreate chain: %v", err)
+ }
+ defer chain.Stop()
+
+ if head := chain.CurrentHeader(); head.Number.Uint64() != uint64(4) {
+ t.Errorf("Head header mismatch: have %d, want %d", head.Number, 4)
+ }
+ if head := chain.CurrentFastBlock(); head.NumberU64() != uint64(4) {
+ t.Errorf("Head fast block mismatch: have %d, want %d", head.NumberU64(), uint64(4))
+ }
+ if head := chain.CurrentBlock(); head.NumberU64() != uint64(1) {
+ t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), uint64(1))
+ }
+
+ // Reinsert B2-B4
+ if _, err := chain.InsertChain(blocks[1:]); err != nil {
+ t.Fatalf("Failed to import canonical chain tail: %v", err)
+ }
+ if head := chain.CurrentHeader(); head.Number.Uint64() != uint64(4) {
+ t.Errorf("Head header mismatch: have %d, want %d", head.Number, 4)
+ }
+ if head := chain.CurrentFastBlock(); head.NumberU64() != uint64(4) {
+ t.Errorf("Head fast block mismatch: have %d, want %d", head.NumberU64(), uint64(4))
+ }
+ if head := chain.CurrentBlock(); head.NumberU64() != uint64(4) {
+ t.Errorf("Head block mismatch: have %d, want %d", head.NumberU64(), uint64(4))
+ }
+ if layer := chain.Snapshots().Snapshot(blocks[2].Root()); layer == nil {
+ t.Error("Failed to regenerate the snapshot of known state")
+ }
+}
diff --git a/core/blockchain_snapshot_test.go b/core/blockchain_snapshot_test.go
index 94ec6879b7..a8044ecb4d 100644
--- a/core/blockchain_snapshot_test.go
+++ b/core/blockchain_snapshot_test.go
@@ -105,7 +105,7 @@ func (basic *snapshotTestBasic) prepare(t *testing.T) (*BlockChain, []*types.Blo
}
if basic.snapshotBlock > 0 && basic.snapshotBlock == point {
// Flushing the entire snap tree into the disk, the
- // relavant (a) snapshot root and (b) snapshot generator
+ // relevant (a) snapshot root and (b) snapshot generator
// will be persisted atomically.
chain.snaps.Cap(blocks[point-1].Root(), 0)
diskRoot, blockRoot := chain.snaps.DiskRoot(), blocks[point-1].Root()
diff --git a/core/blockchain_test.go b/core/blockchain_test.go
index 4e5df633b7..80d07eb30a 100644
--- a/core/blockchain_test.go
+++ b/core/blockchain_test.go
@@ -118,17 +118,21 @@ func testFork(t *testing.T, blockchain *BlockChain, i, n int, full bool, compara
var tdPre, tdPost *big.Int
if full {
- tdPre = blockchain.GetTdByHash(blockchain.CurrentBlock().Hash())
+ cur := blockchain.CurrentBlock()
+ tdPre = blockchain.GetTd(cur.Hash(), cur.NumberU64())
if err := testBlockChainImport(blockChainB, blockchain); err != nil {
t.Fatalf("failed to import forked block chain: %v", err)
}
- tdPost = blockchain.GetTdByHash(blockChainB[len(blockChainB)-1].Hash())
+ last := blockChainB[len(blockChainB)-1]
+ tdPost = blockchain.GetTd(last.Hash(), last.NumberU64())
} else {
- tdPre = blockchain.GetTdByHash(blockchain.CurrentHeader().Hash())
+ cur := blockchain.CurrentHeader()
+ tdPre = blockchain.GetTd(cur.Hash(), cur.Number.Uint64())
if err := testHeaderChainImport(headerChainB, blockchain); err != nil {
t.Fatalf("failed to import forked header chain: %v", err)
}
- tdPost = blockchain.GetTdByHash(headerChainB[len(headerChainB)-1].Hash())
+ last := headerChainB[len(headerChainB)-1]
+ tdPost = blockchain.GetTd(last.Hash(), last.Number.Uint64())
}
// Compare the total difficulties of the chains
comparator(tdPre, tdPost)
@@ -163,8 +167,9 @@ func testBlockChainImport(chain types.Blocks, blockchain *BlockChain) error {
blockchain.reportBlock(block, receipts, err)
return err
}
- blockchain.chainmu.Lock()
- rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTdByHash(block.ParentHash())))
+
+ blockchain.chainmu.MustLock()
+ rawdb.WriteTd(blockchain.db, block.Hash(), block.NumberU64(), new(big.Int).Add(block.Difficulty(), blockchain.GetTd(block.ParentHash(), block.NumberU64()-1)))
rawdb.WriteBlock(blockchain.db, block)
statedb.Commit(false)
blockchain.chainmu.Unlock()
@@ -181,8 +186,8 @@ func testHeaderChainImport(chain []*types.Header, blockchain *BlockChain) error
return err
}
// Manually insert the header into the database, but don't reorganise (allows subsequent testing)
- blockchain.chainmu.Lock()
- rawdb.WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTdByHash(header.ParentHash)))
+ blockchain.chainmu.MustLock()
+ rawdb.WriteTd(blockchain.db, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, blockchain.GetTd(header.ParentHash, header.Number.Uint64()-1)))
rawdb.WriteHeader(blockchain.db, header)
blockchain.chainmu.Unlock()
}
@@ -355,7 +360,7 @@ func TestReorgLongHeaders(t *testing.T) { testReorgLong(t, false) }
func TestReorgLongBlocks(t *testing.T) { testReorgLong(t, true) }
func testReorgLong(t *testing.T, full bool) {
- testReorg(t, []int64{0, 0, -9}, []int64{0, 0, 0, -9}, 393280, full)
+ testReorg(t, []int64{0, 0, -9}, []int64{0, 0, 0, -9}, 393280+params.GenesisDifficulty.Int64(), full)
}
// Tests that reorganising a short difficult chain after a long easy one
@@ -375,7 +380,7 @@ func testReorgShort(t *testing.T, full bool) {
for i := 0; i < len(diff); i++ {
diff[i] = -9
}
- testReorg(t, easy, diff, 12615120, full)
+ testReorg(t, easy, diff, 12615120+params.GenesisDifficulty.Int64(), full)
}
func testReorg(t *testing.T, first, second []int64, td int64, full bool) {
@@ -435,11 +440,13 @@ func testReorg(t *testing.T, first, second []int64, td int64, full bool) {
// Make sure the chain total difficulty is the correct one
want := new(big.Int).Add(blockchain.genesisBlock.Difficulty(), big.NewInt(td))
if full {
- if have := blockchain.GetTdByHash(blockchain.CurrentBlock().Hash()); have.Cmp(want) != 0 {
+ cur := blockchain.CurrentBlock()
+ if have := blockchain.GetTd(cur.Hash(), cur.NumberU64()); have.Cmp(want) != 0 {
t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
}
} else {
- if have := blockchain.GetTdByHash(blockchain.CurrentHeader().Hash()); have.Cmp(want) != 0 {
+ cur := blockchain.CurrentHeader()
+ if have := blockchain.GetTd(cur.Hash(), cur.Number.Uint64()); have.Cmp(want) != 0 {
t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
}
}
@@ -670,14 +677,15 @@ func TestFastVsFullChains(t *testing.T) {
if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(len(blocks)/2)); err != nil {
t.Fatalf("failed to insert receipt %d: %v", n, err)
}
+
// Iterate over all chain data components, and cross reference
for i := 0; i < len(blocks); i++ {
num, hash := blocks[i].NumberU64(), blocks[i].Hash()
- if ftd, atd := fast.GetTdByHash(hash), archive.GetTdByHash(hash); ftd.Cmp(atd) != 0 {
+ if ftd, atd := fast.GetTd(hash, num), archive.GetTd(hash, num); ftd.Cmp(atd) != 0 {
t.Errorf("block #%d [%x]: td mismatch: fastdb %v, archivedb %v", num, hash, ftd, atd)
}
- if antd, artd := ancient.GetTdByHash(hash), archive.GetTdByHash(hash); antd.Cmp(artd) != 0 {
+ if antd, artd := ancient.GetTd(hash, num), archive.GetTd(hash, num); antd.Cmp(artd) != 0 {
t.Errorf("block #%d [%x]: td mismatch: ancientdb %v, archivedb %v", num, hash, antd, artd)
}
if fheader, aheader := fast.GetHeaderByHash(hash), archive.GetHeaderByHash(hash); fheader.Hash() != aheader.Hash() {
@@ -693,10 +701,27 @@ func TestFastVsFullChains(t *testing.T) {
} else if types.CalcUncleHash(fblock.Uncles()) != types.CalcUncleHash(arblock.Uncles()) || types.CalcUncleHash(anblock.Uncles()) != types.CalcUncleHash(arblock.Uncles()) {
t.Errorf("block #%d [%x]: uncles mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, fblock.Uncles(), anblock, arblock.Uncles())
}
- if freceipts, anreceipts, areceipts := rawdb.ReadReceipts(fastDb, hash, *rawdb.ReadHeaderNumber(fastDb, hash), fast.Config()), rawdb.ReadReceipts(ancientDb, hash, *rawdb.ReadHeaderNumber(ancientDb, hash), fast.Config()), rawdb.ReadReceipts(archiveDb, hash, *rawdb.ReadHeaderNumber(archiveDb, hash), fast.Config()); types.DeriveSha(freceipts, trie.NewStackTrie(nil)) != types.DeriveSha(areceipts, trie.NewStackTrie(nil)) {
+
+ // Check receipts.
+ freceipts := rawdb.ReadReceipts(fastDb, hash, num, fast.Config())
+ anreceipts := rawdb.ReadReceipts(ancientDb, hash, num, fast.Config())
+ areceipts := rawdb.ReadReceipts(archiveDb, hash, num, fast.Config())
+ if types.DeriveSha(freceipts, trie.NewStackTrie(nil)) != types.DeriveSha(areceipts, trie.NewStackTrie(nil)) {
t.Errorf("block #%d [%x]: receipts mismatch: fastdb %v, ancientdb %v, archivedb %v", num, hash, freceipts, anreceipts, areceipts)
}
+
+ // Check that hash-to-number mappings are present in all databases.
+ if m := rawdb.ReadHeaderNumber(fastDb, hash); m == nil || *m != num {
+ t.Errorf("block #%d [%x]: wrong hash-to-number mapping in fastdb: %v", num, hash, m)
+ }
+ if m := rawdb.ReadHeaderNumber(ancientDb, hash); m == nil || *m != num {
+ t.Errorf("block #%d [%x]: wrong hash-to-number mapping in ancientdb: %v", num, hash, m)
+ }
+ if m := rawdb.ReadHeaderNumber(archiveDb, hash); m == nil || *m != num {
+ t.Errorf("block #%d [%x]: wrong hash-to-number mapping in archivedb: %v", num, hash, m)
+ }
}
+
// Check that the canonical chains are the same between the databases
for i := 0; i < len(blocks)+1; i++ {
if fhash, ahash := rawdb.ReadCanonicalHash(fastDb, uint64(i)), rawdb.ReadCanonicalHash(archiveDb, uint64(i)); fhash != ahash {
@@ -1639,20 +1664,34 @@ func TestBlockchainRecovery(t *testing.T) {
}
}
-func TestIncompleteAncientReceiptChainInsertion(t *testing.T) {
- // Configure and generate a sample block chain
- var (
- 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}}}
- genesis = gspec.MustCommit(gendb)
- )
- height := uint64(1024)
- blocks, receipts := GenerateChain(gspec.Config, genesis, ethash.NewFaker(), gendb, int(height), nil)
+// This test checks that InsertReceiptChain will roll back correctly when attempting to insert a side chain.
+func TestInsertReceiptChainRollback(t *testing.T) {
+ // Generate forked chain. The returned BlockChain object is used to process the side chain blocks.
+ tmpChain, sideblocks, canonblocks, err := getLongAndShortChains()
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer tmpChain.Stop()
+ // Get the side chain receipts.
+ if _, err := tmpChain.InsertChain(sideblocks); err != nil {
+ t.Fatal("processing side chain failed:", err)
+ }
+ t.Log("sidechain head:", tmpChain.CurrentBlock().Number(), tmpChain.CurrentBlock().Hash())
+ sidechainReceipts := make([]types.Receipts, len(sideblocks))
+ for i, block := range sideblocks {
+ sidechainReceipts[i] = tmpChain.GetReceiptsByHash(block.Hash())
+ }
+ // Get the canon chain receipts.
+ if _, err := tmpChain.InsertChain(canonblocks); err != nil {
+ t.Fatal("processing canon chain failed:", err)
+ }
+ t.Log("canon head:", tmpChain.CurrentBlock().Number(), tmpChain.CurrentBlock().Hash())
+ canonReceipts := make([]types.Receipts, len(canonblocks))
+ for i, block := range canonblocks {
+ canonReceipts[i] = tmpChain.GetReceiptsByHash(block.Hash())
+ }
- // Import the chain as a ancient-first node and ensure all pointers are updated
+ // Set up a BlockChain that uses the ancient store.
frdir, err := ioutil.TempDir("", "")
if err != nil {
t.Fatalf("failed to create temp freezer dir: %v", err)
@@ -1662,38 +1701,43 @@ func TestIncompleteAncientReceiptChainInsertion(t *testing.T) {
if err != nil {
t.Fatalf("failed to create temp freezer db: %v", err)
}
+ gspec := Genesis{Config: params.AllEthashProtocolChanges}
gspec.MustCommit(ancientDb)
- ancient, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
- defer ancient.Stop()
+ ancientChain, _ := NewBlockChain(ancientDb, nil, gspec.Config, ethash.NewFaker(), vm.Config{}, nil, nil)
+ defer ancientChain.Stop()
- headers := make([]*types.Header, len(blocks))
- for i, block := range blocks {
- headers[i] = block.Header()
+ // Import the canonical header chain.
+ canonHeaders := make([]*types.Header, len(canonblocks))
+ for i, block := range canonblocks {
+ canonHeaders[i] = block.Header()
}
- if n, err := ancient.InsertHeaderChain(headers, 1); err != nil {
- t.Fatalf("failed to insert header %d: %v", n, err)
+ if _, err = ancientChain.InsertHeaderChain(canonHeaders, 1); err != nil {
+ t.Fatal("can't import canon headers:", err)
}
- // Abort ancient receipt chain insertion deliberately
- ancient.terminateInsert = func(hash common.Hash, number uint64) bool {
- return number == blocks[len(blocks)/2].NumberU64()
+
+ // Try to insert blocks/receipts of the side chain.
+ _, err = ancientChain.InsertReceiptChain(sideblocks, sidechainReceipts, uint64(len(sideblocks)))
+ if err == nil {
+ t.Fatal("expected error from InsertReceiptChain.")
}
- previousFastBlock := ancient.CurrentFastBlock()
- if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err == nil {
- t.Fatalf("failed to insert receipt %d: %v", n, err)
+ if ancientChain.CurrentFastBlock().NumberU64() != 0 {
+ t.Fatalf("failed to rollback ancient data, want %d, have %d", 0, ancientChain.CurrentFastBlock().NumberU64())
}
- if ancient.CurrentFastBlock().NumberU64() != previousFastBlock.NumberU64() {
- t.Fatalf("failed to rollback ancient data, want %d, have %d", previousFastBlock.NumberU64(), ancient.CurrentFastBlock().NumberU64())
+ if frozen, err := ancientChain.db.Ancients(); err != nil || frozen != 1 {
+ t.Fatalf("failed to truncate ancient data, frozen index is %d", frozen)
}
- if frozen, err := ancient.db.Ancients(); err != nil || frozen != 1 {
- t.Fatalf("failed to truncate ancient data")
+
+ // Insert blocks/receipts of the canonical chain.
+ _, err = ancientChain.InsertReceiptChain(canonblocks, canonReceipts, uint64(len(canonblocks)))
+ if err != nil {
+ t.Fatalf("can't import canon chain receipts: %v", err)
}
- ancient.terminateInsert = nil
- if n, err := ancient.InsertReceiptChain(blocks, receipts, uint64(3*len(blocks)/4)); err != nil {
- t.Fatalf("failed to insert receipt %d: %v", n, err)
- }
- if ancient.CurrentFastBlock().NumberU64() != blocks[len(blocks)-1].NumberU64() {
+ if ancientChain.CurrentFastBlock().NumberU64() != canonblocks[len(canonblocks)-1].NumberU64() {
t.Fatalf("failed to insert ancient recept chain after rollback")
}
+ if frozen, _ := ancientChain.db.Ancients(); frozen != uint64(len(canonblocks))+1 {
+ t.Fatalf("wrong ancients count %d", frozen)
+ }
}
// Tests that importing a very large side fork, which is larger than the canon chain,
@@ -1958,9 +2002,8 @@ func testInsertKnownChainData(t *testing.T, typ string) {
asserter(t, blocks2[len(blocks2)-1])
}
-// getLongAndShortChains returns two chains,
-// A is longer, B is heavier
-func getLongAndShortChains() (*BlockChain, []*types.Block, []*types.Block, error) {
+// getLongAndShortChains returns two chains: A is longer, B is heavier.
+func getLongAndShortChains() (bc *BlockChain, longChain []*types.Block, heavyChain []*types.Block, err error) {
// Generate a canonical chain to act as the main dataset
engine := ethash.NewFaker()
db := rawdb.NewMemoryDatabase()
@@ -1968,7 +2011,7 @@ func getLongAndShortChains() (*BlockChain, []*types.Block, []*types.Block, error
// Generate and import the canonical chain,
// Offset the time, to keep the difficulty low
- longChain, _ := GenerateChain(params.TestChainConfig, genesis, engine, db, 80, func(i int, b *BlockGen) {
+ longChain, _ = GenerateChain(params.TestChainConfig, genesis, engine, db, 80, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{1})
})
diskdb := rawdb.NewMemoryDatabase()
@@ -1982,10 +2025,13 @@ func getLongAndShortChains() (*BlockChain, []*types.Block, []*types.Block, error
// Generate fork chain, make it shorter than canon, with common ancestor pretty early
parentIndex := 3
parent := longChain[parentIndex]
- heavyChain, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 75, func(i int, b *BlockGen) {
+ heavyChainExt, _ := GenerateChain(params.TestChainConfig, parent, engine, db, 75, func(i int, b *BlockGen) {
b.SetCoinbase(common.Address{2})
b.OffsetTime(-9)
})
+ heavyChain = append(heavyChain, longChain[:parentIndex+1]...)
+ heavyChain = append(heavyChain, heavyChainExt...)
+
// Verify that the test is sane
var (
longerTd = new(big.Int)
@@ -2015,6 +2061,7 @@ func getLongAndShortChains() (*BlockChain, []*types.Block, []*types.Block, error
// 1. Have a chain [0 ... N .. X]
// 2. Reorg to shorter but heavier chain [0 ... N ... Y]
// 3. Then there should be no canon mapping for the block at height X
+// 4. The forked block should still be retrievable by hash
func TestReorgToShorterRemovesCanonMapping(t *testing.T) {
chain, canonblocks, sideblocks, err := getLongAndShortChains()
if err != nil {
@@ -2024,6 +2071,7 @@ func TestReorgToShorterRemovesCanonMapping(t *testing.T) {
t.Fatalf("block %d: failed to insert into chain: %v", n, err)
}
canonNum := chain.CurrentBlock().NumberU64()
+ canonHash := chain.CurrentBlock().Hash()
_, err = chain.InsertChain(sideblocks)
if err != nil {
t.Errorf("Got error, %v", err)
@@ -2039,6 +2087,12 @@ func TestReorgToShorterRemovesCanonMapping(t *testing.T) {
if headerByNum := chain.GetHeaderByNumber(canonNum); headerByNum != nil {
t.Errorf("expected header to be gone: %v", headerByNum.Number.Uint64())
}
+ if blockByHash := chain.GetBlockByHash(canonHash); blockByHash == nil {
+ t.Errorf("expected block to be present: %x", blockByHash.Hash())
+ }
+ if headerByHash := chain.GetHeaderByHash(canonHash); headerByHash == nil {
+ t.Errorf("expected header to be present: %x", headerByHash.Hash())
+ }
}
// TestReorgToShorterRemovesCanonMappingHeaderChain is the same scenario
@@ -2058,6 +2112,7 @@ func TestReorgToShorterRemovesCanonMappingHeaderChain(t *testing.T) {
t.Fatalf("header %d: failed to insert into chain: %v", n, err)
}
canonNum := chain.CurrentHeader().Number.Uint64()
+ canonHash := chain.CurrentBlock().Hash()
sideHeaders := make([]*types.Header, len(sideblocks))
for i, block := range sideblocks {
sideHeaders[i] = block.Header()
@@ -2076,6 +2131,12 @@ func TestReorgToShorterRemovesCanonMappingHeaderChain(t *testing.T) {
if headerByNum := chain.GetHeaderByNumber(canonNum); headerByNum != nil {
t.Errorf("expected header to be gone: %v", headerByNum.Number.Uint64())
}
+ if blockByHash := chain.GetBlockByHash(canonHash); blockByHash == nil {
+ t.Errorf("expected block to be present: %x", blockByHash.Hash())
+ }
+ if headerByHash := chain.GetHeaderByHash(canonHash); headerByHash == nil {
+ t.Errorf("expected header to be present: %x", headerByHash.Hash())
+ }
}
func TestTransactionIndices(t *testing.T) {
@@ -2324,7 +2385,7 @@ func benchmarkLargeNumberOfValueToNonexisting(b *testing.B, numTxs, numBlocks in
for txi := 0; txi < numTxs; txi++ {
uniq := uint64(i*numTxs + txi)
recipient := recipientFn(uniq)
- tx, err := types.SignTx(types.NewTransaction(uniq, recipient, big.NewInt(1), params.TxGas, big.NewInt(1), nil), signer, testBankKey)
+ tx, err := types.SignTx(types.NewTransaction(uniq, recipient, big.NewInt(1), params.TxGas, block.header.BaseFee, nil), signer, testBankKey)
if err != nil {
b.Error(err)
}
diff --git a/core/bloombits/generator_test.go b/core/bloombits/generator_test.go
index 88e3ed6b06..883948d12b 100644
--- a/core/bloombits/generator_test.go
+++ b/core/bloombits/generator_test.go
@@ -70,7 +70,7 @@ func BenchmarkGenerator(b *testing.B) {
if err != nil {
b.Fatalf("failed to create bloombit generator: %v", err)
}
- for j, bloom := range input {
+ for j, bloom := range &input {
if err := gen.AddBloom(uint(j), bloom); err != nil {
b.Fatalf("bloom %d: failed to add: %v", i, err)
}
@@ -89,7 +89,7 @@ func BenchmarkGenerator(b *testing.B) {
if err != nil {
b.Fatalf("failed to create bloombit generator: %v", err)
}
- for j, bloom := range input {
+ for j, bloom := range &input {
if err := gen.AddBloom(uint(j), bloom); err != nil {
b.Fatalf("bloom %d: failed to add: %v", i, err)
}
diff --git a/core/error.go b/core/error.go
index 33cf874d15..594f3a26e5 100644
--- a/core/error.go
+++ b/core/error.go
@@ -31,6 +31,8 @@ var (
// ErrNoGenesis is returned when there is no Genesis Block.
ErrNoGenesis = errors.New("genesis not found in chain")
+
+ errSideChainReceipts = errors.New("side blocks can't be accepted as ancient chain data")
)
// List of evm-call-message pre-checking errors. All state transition messages will
diff --git a/core/forkid/forkid_test.go b/core/forkid/forkid_test.go
index 916bffadc0..84c34561d1 100644
--- a/core/forkid/forkid_test.go
+++ b/core/forkid/forkid_test.go
@@ -63,8 +63,10 @@ func TestCreation(t *testing.T) {
{12243999, ID{Hash: checksumToBytes(0xe029e991), Next: 12244000}}, // Last Muir Glacier block
{12244000, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // First Berlin block
{12964999, ID{Hash: checksumToBytes(0x0eb440f6), Next: 12965000}}, // Last Berlin block
- {12965000, ID{Hash: checksumToBytes(0xb715077d), Next: 0}}, // First London block
- {20000000, ID{Hash: checksumToBytes(0xb715077d), Next: 0}}, // Future London block
+ {12965000, ID{Hash: checksumToBytes(0xb715077d), Next: 13773000}}, // First London block
+ {13772999, ID{Hash: checksumToBytes(0xb715077d), Next: 13773000}}, // Last London block
+ {13773000, ID{Hash: checksumToBytes(0x20c327fc), Next: 0}}, /// First Arrow Glacier block
+ {20000000, ID{Hash: checksumToBytes(0x20c327fc), Next: 0}}, // Future Arrow Glacier block
},
},
// Ropsten test cases
@@ -205,11 +207,11 @@ func TestValidation(t *testing.T) {
// Local is mainnet Petersburg, remote is Rinkeby Petersburg.
{7987396, ID{Hash: checksumToBytes(0xafec6b27), Next: 0}, ErrLocalIncompatibleOrStale},
- // Local is mainnet London, far in the future. Remote announces Gopherium (non existing fork)
+ // Local is mainnet Arrow Glacier, far in the future. Remote announces Gopherium (non existing fork)
// at some future block 88888888, for itself, but past block for local. Local is incompatible.
//
// This case detects non-upgraded nodes with majority hash power (typical Ropsten mess).
- {88888888, ID{Hash: checksumToBytes(0xb715077d), Next: 88888888}, ErrLocalIncompatibleOrStale},
+ {88888888, ID{Hash: checksumToBytes(0x20c327fc), Next: 88888888}, ErrLocalIncompatibleOrStale},
// Local is mainnet Byzantium. Remote is also in Byzantium, but announces Gopherium (non existing
// fork) at block 7279999, before Petersburg. Local is incompatible.
diff --git a/core/genesis.go b/core/genesis.go
index c1f226c34a..1c1534fbb5 100644
--- a/core/genesis.go
+++ b/core/genesis.go
@@ -158,7 +158,7 @@ func SetupGenesisBlock(db ethdb.Database, genesis *Genesis) (*params.ChainConfig
return SetupGenesisBlockWithOverride(db, genesis, nil)
}
-func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideLondon *big.Int) (*params.ChainConfig, common.Hash, error) {
+func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, overrideArrowGlacier *big.Int) (*params.ChainConfig, common.Hash, error) {
if genesis != nil && genesis.Config == nil {
return params.AllEthashProtocolChanges, common.Hash{}, errGenesisNoConfig
}
@@ -204,8 +204,8 @@ func SetupGenesisBlockWithOverride(db ethdb.Database, genesis *Genesis, override
}
// Get the existing chain configuration.
newcfg := genesis.configOrDefault(stored)
- if overrideLondon != nil {
- newcfg.LondonBlock = overrideLondon
+ if overrideArrowGlacier != nil {
+ newcfg.ArrowGlacierBlock = overrideArrowGlacier
}
if err := newcfg.CheckConfigForkOrder(); err != nil {
return newcfg, common.Hash{}, err
@@ -310,7 +310,7 @@ func (g *Genesis) ToBlock(db ethdb.Database) *types.Block {
func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) {
block := g.ToBlock(db)
if block.Number().Sign() != 0 {
- return nil, fmt.Errorf("can't commit genesis block with number > 0")
+ return nil, errors.New("can't commit genesis block with number > 0")
}
config := g.Config
if config == nil {
@@ -319,7 +319,10 @@ func (g *Genesis) Commit(db ethdb.Database) (*types.Block, error) {
if err := config.CheckConfigForkOrder(); err != nil {
return nil, err
}
- rawdb.WriteTd(db, block.Hash(), block.NumberU64(), g.Difficulty)
+ if config.Clique != nil && len(block.Extra()) == 0 {
+ return nil, errors.New("can't start clique chain without signers")
+ }
+ rawdb.WriteTd(db, block.Hash(), block.NumberU64(), block.Difficulty())
rawdb.WriteBlock(db, block)
rawdb.WriteReceipts(db, block.Hash(), block.NumberU64(), nil)
rawdb.WriteCanonicalHash(db, block.Hash(), block.NumberU64())
diff --git a/core/genesis_test.go b/core/genesis_test.go
index 52c4384abe..056c1c195f 100644
--- a/core/genesis_test.go
+++ b/core/genesis_test.go
@@ -39,6 +39,22 @@ func TestDefaultGenesisBlock(t *testing.T) {
if block.Hash() != params.RopstenGenesisHash {
t.Errorf("wrong ropsten genesis hash, got %v, want %v", block.Hash(), params.RopstenGenesisHash)
}
+ block = DefaultRinkebyGenesisBlock().ToBlock(nil)
+ if block.Hash() != params.RinkebyGenesisHash {
+ t.Errorf("wrong rinkeby genesis hash, got %v, want %v", block.Hash(), params.RinkebyGenesisHash)
+ }
+ block = DefaultGoerliGenesisBlock().ToBlock(nil)
+ if block.Hash() != params.GoerliGenesisHash {
+ t.Errorf("wrong goerli genesis hash, got %v, want %v", block.Hash(), params.GoerliGenesisHash)
+ }
+}
+
+func TestInvalidCliqueConfig(t *testing.T) {
+ block := DefaultGoerliGenesisBlock()
+ block.ExtraData = []byte{}
+ if _, err := block.Commit(nil); err == nil {
+ t.Fatal("Expected error on invalid clique config")
+ }
}
func TestSetupGenesis(t *testing.T) {
@@ -193,3 +209,33 @@ func TestGenesisHashes(t *testing.T) {
}
}
}
+
+func TestGenesis_Commit(t *testing.T) {
+ genesis := &Genesis{
+ BaseFee: big.NewInt(params.InitialBaseFee),
+ Config: params.TestChainConfig,
+ // difficulty is nil
+ }
+
+ db := rawdb.NewMemoryDatabase()
+ genesisBlock, err := genesis.Commit(db)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if genesis.Difficulty != nil {
+ t.Fatalf("assumption wrong")
+ }
+
+ // This value should have been set as default in the ToBlock method.
+ if genesisBlock.Difficulty().Cmp(params.GenesisDifficulty) != 0 {
+ t.Errorf("assumption wrong: want: %d, got: %v", params.GenesisDifficulty, genesisBlock.Difficulty())
+ }
+
+ // Expect the stored total difficulty to be the difficulty of the genesis block.
+ stored := rawdb.ReadTd(db, genesisBlock.Hash(), genesisBlock.NumberU64())
+
+ if stored.Cmp(genesisBlock.Difficulty()) != 0 {
+ t.Errorf("inequal difficulty; stored: %v, genesisBlock: %v", stored, genesisBlock.Difficulty())
+ }
+}
diff --git a/core/headerchain.go b/core/headerchain.go
index 7ef7dd43f7..9f2b708d0a 100644
--- a/core/headerchain.go
+++ b/core/headerchain.go
@@ -394,29 +394,6 @@ func (hc *HeaderChain) InsertHeaderChain(chain []*types.Header, start time.Time)
return res.status, err
}
-// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
-// hash, fetching towards the genesis block.
-func (hc *HeaderChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
- // Get the origin header from which to fetch
- header := hc.GetHeaderByHash(hash)
- if header == nil {
- return nil
- }
- // Iterate the headers until enough is collected or the genesis reached
- chain := make([]common.Hash, 0, max)
- for i := uint64(0); i < max; i++ {
- next := header.ParentHash
- if header = hc.GetHeader(next, header.Number.Uint64()-1); header == nil {
- break
- }
- chain = append(chain, next)
- if header.Number.Sign() == 0 {
- break
- }
- }
- return chain
-}
-
// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
// number of blocks to be individually checked before we reach the canonical chain.
@@ -472,16 +449,6 @@ func (hc *HeaderChain) GetTd(hash common.Hash, number uint64) *big.Int {
return td
}
-// GetTdByHash retrieves a block's total difficulty in the canonical chain from the
-// database by hash, caching it if found.
-func (hc *HeaderChain) GetTdByHash(hash common.Hash) *big.Int {
- number := hc.GetBlockNumber(hash)
- if number == nil {
- return nil
- }
- return hc.GetTd(hash, *number)
-}
-
// GetHeader retrieves a block header from the database by hash and number,
// caching it if found.
func (hc *HeaderChain) GetHeader(hash common.Hash, number uint64) *types.Header {
diff --git a/core/mkalloc.go b/core/mkalloc.go
index 5118a4fcb3..df167d7082 100644
--- a/core/mkalloc.go
+++ b/core/mkalloc.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 none
// +build none
/*
diff --git a/core/rawdb/accessors_chain.go b/core/rawdb/accessors_chain.go
index 76132bf37e..7f26c3a420 100644
--- a/core/rawdb/accessors_chain.go
+++ b/core/rawdb/accessors_chain.go
@@ -19,6 +19,8 @@ package rawdb
import (
"bytes"
"encoding/binary"
+ "errors"
+ "fmt"
"math/big"
"sort"
@@ -33,20 +35,15 @@ import (
// ReadCanonicalHash retrieves the hash assigned to a canonical block number.
func ReadCanonicalHash(db ethdb.Reader, number uint64) common.Hash {
- data, _ := db.Ancient(freezerHashTable, number)
- if len(data) == 0 {
- data, _ = db.Get(headerHashKey(number))
- // In the background freezer is moving data from leveldb to flatten files.
- // So during the first check for ancient db, the data is not yet in there,
- // but when we reach into leveldb, the data was already moved. That would
- // result in a not found error.
+ var data []byte
+ db.ReadAncients(func(reader ethdb.AncientReader) error {
+ data, _ = reader.Ancient(freezerHashTable, number)
if len(data) == 0 {
- data, _ = db.Ancient(freezerHashTable, number)
+ // Get it by hash from leveldb
+ data, _ = db.Get(headerHashKey(number))
}
- }
- if len(data) == 0 {
- return common.Hash{}
- }
+ return nil
+ })
return common.BytesToHash(data)
}
@@ -81,6 +78,37 @@ func ReadAllHashes(db ethdb.Iteratee, number uint64) []common.Hash {
return hashes
}
+type NumberHash struct {
+ Number uint64
+ Hash common.Hash
+}
+
+// ReadAllHashes retrieves all the hashes assigned to blocks at a certain heights,
+// both canonical and reorged forks included.
+// This method considers both limits to be _inclusive_.
+func ReadAllHashesInRange(db ethdb.Iteratee, first, last uint64) []*NumberHash {
+ var (
+ start = encodeBlockNumber(first)
+ keyLength = len(headerPrefix) + 8 + 32
+ hashes = make([]*NumberHash, 0, 1+last-first)
+ it = db.NewIterator(headerPrefix, start)
+ )
+ defer it.Release()
+ for it.Next() {
+ key := it.Key()
+ if len(key) != keyLength {
+ continue
+ }
+ num := binary.BigEndian.Uint64(key[len(headerPrefix) : len(headerPrefix)+8])
+ if num > last {
+ break
+ }
+ hash := common.BytesToHash(key[len(key)-32:])
+ hashes = append(hashes, &NumberHash{num, hash})
+ }
+ return hashes
+}
+
// ReadAllCanonicalHashes retrieves all canonical number and hash mappings at the
// certain chain range. If the accumulated entries reaches the given threshold,
// abort the iteration and return the semi-finish result.
@@ -271,32 +299,25 @@ func WriteFastTxLookupLimit(db ethdb.KeyValueWriter, number uint64) {
// ReadHeaderRLP retrieves a block header in its raw RLP database encoding.
func ReadHeaderRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
- // First try to look up the data in ancient database. Extra hash
- // comparison is necessary since ancient database only maintains
- // the canonical data.
- data, _ := db.Ancient(freezerHeaderTable, number)
- if len(data) > 0 && crypto.Keccak256Hash(data) == hash {
- return data
- }
- // Then try to look up the data in leveldb.
- data, _ = db.Get(headerKey(number, hash))
- if len(data) > 0 {
- return data
- }
- // In the background freezer is moving data from leveldb to flatten files.
- // So during the first check for ancient db, the data is not yet in there,
- // but when we reach into leveldb, the data was already moved. That would
- // result in a not found error.
- data, _ = db.Ancient(freezerHeaderTable, number)
- if len(data) > 0 && crypto.Keccak256Hash(data) == hash {
- return data
- }
- return nil // Can't find the data anywhere.
+ var data []byte
+ db.ReadAncients(func(reader ethdb.AncientReader) error {
+ // First try to look up the data in ancient database. Extra hash
+ // comparison is necessary since ancient database only maintains
+ // the canonical data.
+ data, _ = reader.Ancient(freezerHeaderTable, number)
+ if len(data) > 0 && crypto.Keccak256Hash(data) == hash {
+ return nil
+ }
+ // If not, try reading from leveldb
+ data, _ = db.Get(headerKey(number, hash))
+ return nil
+ })
+ return data
}
// HasHeader verifies the existence of a block header corresponding to the hash.
func HasHeader(db ethdb.Reader, hash common.Hash, number uint64) bool {
- if has, err := db.Ancient(freezerHashTable, number); err == nil && common.BytesToHash(has) == hash {
+ if isCanon(db, number, hash) {
return true
}
if has, err := db.Has(headerKey(number, hash)); !has || err != nil {
@@ -356,53 +377,48 @@ func deleteHeaderWithoutNumber(db ethdb.KeyValueWriter, hash common.Hash, number
}
}
+// isCanon is an internal utility method, to check whether the given number/hash
+// is part of the ancient (canon) set.
+func isCanon(reader ethdb.AncientReader, number uint64, hash common.Hash) bool {
+ h, err := reader.Ancient(freezerHashTable, number)
+ if err != nil {
+ return false
+ }
+ return bytes.Equal(h, hash[:])
+}
+
// ReadBodyRLP retrieves the block body (transactions and uncles) in RLP encoding.
func ReadBodyRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
// First try to look up the data in ancient database. Extra hash
// comparison is necessary since ancient database only maintains
// the canonical data.
- data, _ := db.Ancient(freezerBodiesTable, number)
- if len(data) > 0 {
- h, _ := db.Ancient(freezerHashTable, number)
- if common.BytesToHash(h) == hash {
- return data
+ var data []byte
+ db.ReadAncients(func(reader ethdb.AncientReader) error {
+ // Check if the data is in ancients
+ if isCanon(reader, number, hash) {
+ data, _ = reader.Ancient(freezerBodiesTable, number)
+ return nil
}
- }
- // Then try to look up the data in leveldb.
- data, _ = db.Get(blockBodyKey(number, hash))
- if len(data) > 0 {
- return data
- }
- // In the background freezer is moving data from leveldb to flatten files.
- // So during the first check for ancient db, the data is not yet in there,
- // but when we reach into leveldb, the data was already moved. That would
- // result in a not found error.
- data, _ = db.Ancient(freezerBodiesTable, number)
- if len(data) > 0 {
- h, _ := db.Ancient(freezerHashTable, number)
- if common.BytesToHash(h) == hash {
- return data
- }
- }
- return nil // Can't find the data anywhere.
+ // If not, try reading from leveldb
+ data, _ = db.Get(blockBodyKey(number, hash))
+ return nil
+ })
+ return data
}
// ReadCanonicalBodyRLP retrieves the block body (transactions and uncles) for the canonical
// block at number, in RLP encoding.
func ReadCanonicalBodyRLP(db ethdb.Reader, number uint64) rlp.RawValue {
- // If it's an ancient one, we don't need the canonical hash
- data, _ := db.Ancient(freezerBodiesTable, number)
- if len(data) == 0 {
- // Need to get the hash
- data, _ = db.Get(blockBodyKey(number, ReadCanonicalHash(db, number)))
- // In the background freezer is moving data from leveldb to flatten files.
- // So during the first check for ancient db, the data is not yet in there,
- // but when we reach into leveldb, the data was already moved. That would
- // result in a not found error.
- if len(data) == 0 {
- data, _ = db.Ancient(freezerBodiesTable, number)
+ var data []byte
+ db.ReadAncients(func(reader ethdb.AncientReader) error {
+ data, _ = reader.Ancient(freezerBodiesTable, number)
+ if len(data) > 0 {
+ return nil
}
- }
+ // Get it by hash from leveldb
+ data, _ = db.Get(blockBodyKey(number, ReadCanonicalHash(db, number)))
+ return nil
+ })
return data
}
@@ -415,7 +431,7 @@ func WriteBodyRLP(db ethdb.KeyValueWriter, hash common.Hash, number uint64, rlp
// HasBody verifies the existence of a block body corresponding to the hash.
func HasBody(db ethdb.Reader, hash common.Hash, number uint64) bool {
- if has, err := db.Ancient(freezerHashTable, number); err == nil && common.BytesToHash(has) == hash {
+ if isCanon(db, number, hash) {
return true
}
if has, err := db.Has(blockBodyKey(number, hash)); !has || err != nil {
@@ -456,33 +472,18 @@ func DeleteBody(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
// ReadTdRLP retrieves a block's total difficulty corresponding to the hash in RLP encoding.
func ReadTdRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
- // First try to look up the data in ancient database. Extra hash
- // comparison is necessary since ancient database only maintains
- // the canonical data.
- data, _ := db.Ancient(freezerDifficultyTable, number)
- if len(data) > 0 {
- h, _ := db.Ancient(freezerHashTable, number)
- if common.BytesToHash(h) == hash {
- return data
+ var data []byte
+ db.ReadAncients(func(reader ethdb.AncientReader) error {
+ // Check if the data is in ancients
+ if isCanon(reader, number, hash) {
+ data, _ = reader.Ancient(freezerDifficultyTable, number)
+ return nil
}
- }
- // Then try to look up the data in leveldb.
- data, _ = db.Get(headerTDKey(number, hash))
- if len(data) > 0 {
- return data
- }
- // In the background freezer is moving data from leveldb to flatten files.
- // So during the first check for ancient db, the data is not yet in there,
- // but when we reach into leveldb, the data was already moved. That would
- // result in a not found error.
- data, _ = db.Ancient(freezerDifficultyTable, number)
- if len(data) > 0 {
- h, _ := db.Ancient(freezerHashTable, number)
- if common.BytesToHash(h) == hash {
- return data
- }
- }
- return nil // Can't find the data anywhere.
+ // If not, try reading from leveldb
+ data, _ = db.Get(headerTDKey(number, hash))
+ return nil
+ })
+ return data
}
// ReadTd retrieves a block's total difficulty corresponding to the hash.
@@ -520,7 +521,7 @@ func DeleteTd(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
// HasReceipts verifies the existence of all the transaction receipts belonging
// to a block.
func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool {
- if has, err := db.Ancient(freezerHashTable, number); err == nil && common.BytesToHash(has) == hash {
+ if isCanon(db, number, hash) {
return true
}
if has, err := db.Has(blockReceiptsKey(number, hash)); !has || err != nil {
@@ -531,33 +532,18 @@ func HasReceipts(db ethdb.Reader, hash common.Hash, number uint64) bool {
// ReadReceiptsRLP retrieves all the transaction receipts belonging to a block in RLP encoding.
func ReadReceiptsRLP(db ethdb.Reader, hash common.Hash, number uint64) rlp.RawValue {
- // First try to look up the data in ancient database. Extra hash
- // comparison is necessary since ancient database only maintains
- // the canonical data.
- data, _ := db.Ancient(freezerReceiptTable, number)
- if len(data) > 0 {
- h, _ := db.Ancient(freezerHashTable, number)
- if common.BytesToHash(h) == hash {
- return data
+ var data []byte
+ db.ReadAncients(func(reader ethdb.AncientReader) error {
+ // Check if the data is in ancients
+ if isCanon(reader, number, hash) {
+ data, _ = reader.Ancient(freezerReceiptTable, number)
+ return nil
}
- }
- // Then try to look up the data in leveldb.
- data, _ = db.Get(blockReceiptsKey(number, hash))
- if len(data) > 0 {
- return data
- }
- // In the background freezer is moving data from leveldb to flatten files.
- // So during the first check for ancient db, the data is not yet in there,
- // but when we reach into leveldb, the data was already moved. That would
- // result in a not found error.
- data, _ = db.Ancient(freezerReceiptTable, number)
- if len(data) > 0 {
- h, _ := db.Ancient(freezerHashTable, number)
- if common.BytesToHash(h) == hash {
- return data
- }
- }
- return nil // Can't find the data anywhere.
+ // If not, try reading from leveldb
+ data, _ = db.Get(blockReceiptsKey(number, hash))
+ return nil
+ })
+ return data
}
// ReadRawReceipts retrieves all the transaction receipts belonging to a block.
@@ -631,6 +617,86 @@ func DeleteReceipts(db ethdb.KeyValueWriter, hash common.Hash, number uint64) {
}
}
+// storedReceiptRLP is the storage encoding of a receipt.
+// Re-definition in core/types/receipt.go.
+type storedReceiptRLP struct {
+ PostStateOrStatus []byte
+ CumulativeGasUsed uint64
+ Logs []*types.LogForStorage
+}
+
+// ReceiptLogs is a barebone version of ReceiptForStorage which only keeps
+// the list of logs. When decoding a stored receipt into this object we
+// avoid creating the bloom filter.
+type receiptLogs struct {
+ Logs []*types.Log
+}
+
+// DecodeRLP implements rlp.Decoder.
+func (r *receiptLogs) DecodeRLP(s *rlp.Stream) error {
+ var stored storedReceiptRLP
+ if err := s.Decode(&stored); err != nil {
+ return err
+ }
+ r.Logs = make([]*types.Log, len(stored.Logs))
+ for i, log := range stored.Logs {
+ r.Logs[i] = (*types.Log)(log)
+ }
+ return nil
+}
+
+// DeriveLogFields fills the logs in receiptLogs with information such as block number, txhash, etc.
+func deriveLogFields(receipts []*receiptLogs, hash common.Hash, number uint64, txs types.Transactions) error {
+ logIndex := uint(0)
+ if len(txs) != len(receipts) {
+ return errors.New("transaction and receipt count mismatch")
+ }
+ for i := 0; i < len(receipts); i++ {
+ txHash := txs[i].Hash()
+ // The derived log fields can simply be set from the block and transaction
+ for j := 0; j < len(receipts[i].Logs); j++ {
+ receipts[i].Logs[j].BlockNumber = number
+ receipts[i].Logs[j].BlockHash = hash
+ receipts[i].Logs[j].TxHash = txHash
+ receipts[i].Logs[j].TxIndex = uint(i)
+ receipts[i].Logs[j].Index = logIndex
+ logIndex++
+ }
+ }
+ return nil
+}
+
+// ReadLogs retrieves the logs for all transactions in a block. The log fields
+// are populated with metadata. In case the receipts or the block body
+// are not found, a nil is returned.
+func ReadLogs(db ethdb.Reader, hash common.Hash, number uint64) [][]*types.Log {
+ // Retrieve the flattened receipt slice
+ data := ReadReceiptsRLP(db, hash, number)
+ if len(data) == 0 {
+ return nil
+ }
+ receipts := []*receiptLogs{}
+ if err := rlp.DecodeBytes(data, &receipts); err != nil {
+ log.Error("Invalid receipt array RLP", "hash", hash, "err", err)
+ return nil
+ }
+
+ body := ReadBody(db, hash, number)
+ if body == nil {
+ log.Error("Missing body but have receipt", "hash", hash, "number", number)
+ return nil
+ }
+ if err := deriveLogFields(receipts, hash, number, body.Transactions); err != nil {
+ log.Error("Failed to derive block receipts fields", "hash", hash, "number", number, "err", err)
+ return nil
+ }
+ logs := make([][]*types.Log, len(receipts))
+ for i, receipt := range receipts {
+ logs[i] = receipt.Logs
+ }
+ return logs
+}
+
// ReadBlock retrieves an entire block corresponding to the hash, assembling it
// back from the stored header and body. If either the header or body could not
// be retrieved nil is returned.
@@ -656,34 +722,48 @@ func WriteBlock(db ethdb.KeyValueWriter, block *types.Block) {
}
// WriteAncientBlock writes entire block data into ancient store and returns the total written size.
-func WriteAncientBlock(db ethdb.AncientWriter, block *types.Block, receipts types.Receipts, td *big.Int) int {
- // Encode all block components to RLP format.
- headerBlob, err := rlp.EncodeToBytes(block.Header())
- if err != nil {
- log.Crit("Failed to RLP encode block header", "err", err)
+func WriteAncientBlocks(db ethdb.AncientWriter, blocks []*types.Block, receipts []types.Receipts, td *big.Int) (int64, error) {
+ var (
+ tdSum = new(big.Int).Set(td)
+ stReceipts []*types.ReceiptForStorage
+ )
+ return db.ModifyAncients(func(op ethdb.AncientWriteOp) error {
+ for i, block := range blocks {
+ // Convert receipts to storage format and sum up total difficulty.
+ stReceipts = stReceipts[:0]
+ for _, receipt := range receipts[i] {
+ stReceipts = append(stReceipts, (*types.ReceiptForStorage)(receipt))
+ }
+ header := block.Header()
+ if i > 0 {
+ tdSum.Add(tdSum, header.Difficulty)
+ }
+ if err := writeAncientBlock(op, block, header, stReceipts, tdSum); err != nil {
+ return err
+ }
+ }
+ return nil
+ })
+}
+
+func writeAncientBlock(op ethdb.AncientWriteOp, block *types.Block, header *types.Header, receipts []*types.ReceiptForStorage, td *big.Int) error {
+ num := block.NumberU64()
+ if err := op.AppendRaw(freezerHashTable, num, block.Hash().Bytes()); err != nil {
+ return fmt.Errorf("can't add block %d hash: %v", num, err)
}
- bodyBlob, err := rlp.EncodeToBytes(block.Body())
- if err != nil {
- log.Crit("Failed to RLP encode body", "err", err)
+ if err := op.Append(freezerHeaderTable, num, header); err != nil {
+ return fmt.Errorf("can't append block header %d: %v", num, err)
}
- storageReceipts := make([]*types.ReceiptForStorage, len(receipts))
- for i, receipt := range receipts {
- storageReceipts[i] = (*types.ReceiptForStorage)(receipt)
+ if err := op.Append(freezerBodiesTable, num, block.Body()); err != nil {
+ return fmt.Errorf("can't append block body %d: %v", num, err)
}
- receiptBlob, err := rlp.EncodeToBytes(storageReceipts)
- if err != nil {
- log.Crit("Failed to RLP encode block receipts", "err", err)
+ if err := op.Append(freezerReceiptTable, num, receipts); err != nil {
+ return fmt.Errorf("can't append block %d receipts: %v", num, err)
}
- tdBlob, err := rlp.EncodeToBytes(td)
- if err != nil {
- log.Crit("Failed to RLP encode block total difficulty", "err", err)
+ if err := op.Append(freezerDifficultyTable, num, td); err != nil {
+ return fmt.Errorf("can't append block %d total difficulty: %v", num, err)
}
- // Write all blob to flatten files.
- err = db.AppendAncient(block.NumberU64(), block.Hash().Bytes(), headerBlob, bodyBlob, receiptBlob, tdBlob)
- if err != nil {
- log.Crit("Failed to write block data to ancient store", "err", err)
- }
- return len(headerBlob) + len(bodyBlob) + len(receiptBlob) + len(tdBlob) + common.HashLength
+ return nil
}
// DeleteBlock removes all block data associated with a hash.
diff --git a/core/rawdb/accessors_chain_test.go b/core/rawdb/accessors_chain_test.go
index f20e8b1fff..4b173c55ee 100644
--- a/core/rawdb/accessors_chain_test.go
+++ b/core/rawdb/accessors_chain_test.go
@@ -29,6 +29,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/params"
"github.com/ethereum/go-ethereum/rlp"
"golang.org/x/crypto/sha3"
@@ -438,7 +439,7 @@ func TestAncientStorage(t *testing.T) {
if err != nil {
t.Fatalf("failed to create temp freezer dir: %v", err)
}
- defer os.Remove(frdir)
+ defer os.RemoveAll(frdir)
db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false)
if err != nil {
@@ -467,8 +468,10 @@ func TestAncientStorage(t *testing.T) {
if blob := ReadTdRLP(db, hash, number); len(blob) > 0 {
t.Fatalf("non existent td returned")
}
+
// Write and verify the header in the database
- WriteAncientBlock(db, block, nil, big.NewInt(100))
+ WriteAncientBlocks(db, []*types.Block{block}, []types.Receipts{nil}, big.NewInt(100))
+
if blob := ReadHeaderRLP(db, hash, number); len(blob) == 0 {
t.Fatalf("no header returned")
}
@@ -481,6 +484,7 @@ func TestAncientStorage(t *testing.T) {
if blob := ReadTdRLP(db, hash, number); len(blob) == 0 {
t.Fatalf("no td returned")
}
+
// Use a fake hash for data retrieval, nothing should be returned.
fakeHash := common.BytesToHash([]byte{0x01, 0x02, 0x03})
if blob := ReadHeaderRLP(db, fakeHash, number); len(blob) != 0 {
@@ -528,3 +532,354 @@ func TestCanonicalHashIteration(t *testing.T) {
}
}
}
+
+func TestHashesInRange(t *testing.T) {
+ mkHeader := func(number, seq int) *types.Header {
+ h := types.Header{
+ Difficulty: new(big.Int),
+ Number: big.NewInt(int64(number)),
+ GasLimit: uint64(seq),
+ }
+ return &h
+ }
+ db := NewMemoryDatabase()
+ // For each number, write N versions of that particular number
+ total := 0
+ for i := 0; i < 15; i++ {
+ for ii := 0; ii < i; ii++ {
+ WriteHeader(db, mkHeader(i, ii))
+ total++
+ }
+ }
+ if have, want := len(ReadAllHashesInRange(db, 10, 10)), 10; have != want {
+ t.Fatalf("Wrong number of hashes read, want %d, got %d", want, have)
+ }
+ if have, want := len(ReadAllHashesInRange(db, 10, 9)), 0; have != want {
+ t.Fatalf("Wrong number of hashes read, want %d, got %d", want, have)
+ }
+ if have, want := len(ReadAllHashesInRange(db, 0, 100)), total; have != want {
+ t.Fatalf("Wrong number of hashes read, want %d, got %d", want, have)
+ }
+ if have, want := len(ReadAllHashesInRange(db, 9, 10)), 9+10; have != want {
+ t.Fatalf("Wrong number of hashes read, want %d, got %d", want, have)
+ }
+ if have, want := len(ReadAllHashes(db, 10)), 10; have != want {
+ t.Fatalf("Wrong number of hashes read, want %d, got %d", want, have)
+ }
+ if have, want := len(ReadAllHashes(db, 16)), 0; have != want {
+ t.Fatalf("Wrong number of hashes read, want %d, got %d", want, have)
+ }
+ if have, want := len(ReadAllHashes(db, 1)), 1; have != want {
+ t.Fatalf("Wrong number of hashes read, want %d, got %d", want, have)
+ }
+}
+
+// This measures the write speed of the WriteAncientBlocks operation.
+func BenchmarkWriteAncientBlocks(b *testing.B) {
+ // Open freezer database.
+ frdir, err := ioutil.TempDir("", "")
+ if err != nil {
+ b.Fatalf("failed to create temp freezer dir: %v", err)
+ }
+ defer os.RemoveAll(frdir)
+ db, err := NewDatabaseWithFreezer(NewMemoryDatabase(), frdir, "", false)
+ if err != nil {
+ b.Fatalf("failed to create database with ancient backend")
+ }
+
+ // Create the data to insert. The blocks must have consecutive numbers, so we create
+ // all of them ahead of time. However, there is no need to create receipts
+ // individually for each block, just make one batch here and reuse it for all writes.
+ const batchSize = 128
+ const blockTxs = 20
+ allBlocks := makeTestBlocks(b.N, blockTxs)
+ batchReceipts := makeTestReceipts(batchSize, blockTxs)
+ b.ResetTimer()
+
+ // The benchmark loop writes batches of blocks, but note that the total block count is
+ // b.N. This means the resulting ns/op measurement is the time it takes to write a
+ // single block and its associated data.
+ var td = big.NewInt(55)
+ var totalSize int64
+ for i := 0; i < b.N; i += batchSize {
+ length := batchSize
+ if i+batchSize > b.N {
+ length = b.N - i
+ }
+
+ blocks := allBlocks[i : i+length]
+ receipts := batchReceipts[:length]
+ writeSize, err := WriteAncientBlocks(db, blocks, receipts, td)
+ if err != nil {
+ b.Fatal(err)
+ }
+ totalSize += writeSize
+ }
+
+ // Enable MB/s reporting.
+ b.SetBytes(totalSize / int64(b.N))
+}
+
+// makeTestBlocks creates fake blocks for the ancient write benchmark.
+func makeTestBlocks(nblock int, txsPerBlock int) []*types.Block {
+ key, _ := crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ signer := types.LatestSignerForChainID(big.NewInt(8))
+
+ // Create transactions.
+ txs := make([]*types.Transaction, txsPerBlock)
+ for i := 0; i < len(txs); i++ {
+ var err error
+ to := common.Address{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
+ txs[i], err = types.SignNewTx(key, signer, &types.LegacyTx{
+ Nonce: 2,
+ GasPrice: big.NewInt(30000),
+ Gas: 0x45454545,
+ To: &to,
+ })
+ if err != nil {
+ panic(err)
+ }
+ }
+
+ // Create the blocks.
+ blocks := make([]*types.Block, nblock)
+ for i := 0; i < nblock; i++ {
+ header := &types.Header{
+ Number: big.NewInt(int64(i)),
+ Extra: []byte("test block"),
+ }
+ blocks[i] = types.NewBlockWithHeader(header).WithBody(txs, nil)
+ blocks[i].Hash() // pre-cache the block hash
+ }
+ return blocks
+}
+
+// makeTestReceipts creates fake receipts for the ancient write benchmark.
+func makeTestReceipts(n int, nPerBlock int) []types.Receipts {
+ receipts := make([]*types.Receipt, nPerBlock)
+ for i := 0; i < len(receipts); i++ {
+ receipts[i] = &types.Receipt{
+ Status: types.ReceiptStatusSuccessful,
+ CumulativeGasUsed: 0x888888888,
+ Logs: make([]*types.Log, 5),
+ }
+ }
+ allReceipts := make([]types.Receipts, n)
+ for i := 0; i < n; i++ {
+ allReceipts[i] = receipts
+ }
+ return allReceipts
+}
+
+type fullLogRLP struct {
+ Address common.Address
+ Topics []common.Hash
+ Data []byte
+ BlockNumber uint64
+ TxHash common.Hash
+ TxIndex uint
+ BlockHash common.Hash
+ Index uint
+}
+
+func newFullLogRLP(l *types.Log) *fullLogRLP {
+ return &fullLogRLP{
+ Address: l.Address,
+ Topics: l.Topics,
+ Data: l.Data,
+ BlockNumber: l.BlockNumber,
+ TxHash: l.TxHash,
+ TxIndex: l.TxIndex,
+ BlockHash: l.BlockHash,
+ Index: l.Index,
+ }
+}
+
+// Tests that logs associated with a single block can be retrieved.
+func TestReadLogs(t *testing.T) {
+ db := NewMemoryDatabase()
+
+ // Create a live block since we need metadata to reconstruct the receipt
+ tx1 := types.NewTransaction(1, common.HexToAddress("0x1"), big.NewInt(1), 1, big.NewInt(1), nil)
+ tx2 := types.NewTransaction(2, common.HexToAddress("0x2"), big.NewInt(2), 2, big.NewInt(2), nil)
+
+ body := &types.Body{Transactions: types.Transactions{tx1, tx2}}
+
+ // Create the two receipts to manage afterwards
+ receipt1 := &types.Receipt{
+ Status: types.ReceiptStatusFailed,
+ CumulativeGasUsed: 1,
+ Logs: []*types.Log{
+ {Address: common.BytesToAddress([]byte{0x11})},
+ {Address: common.BytesToAddress([]byte{0x01, 0x11})},
+ },
+ TxHash: tx1.Hash(),
+ ContractAddress: common.BytesToAddress([]byte{0x01, 0x11, 0x11}),
+ GasUsed: 111111,
+ }
+ receipt1.Bloom = types.CreateBloom(types.Receipts{receipt1})
+
+ receipt2 := &types.Receipt{
+ PostState: common.Hash{2}.Bytes(),
+ CumulativeGasUsed: 2,
+ Logs: []*types.Log{
+ {Address: common.BytesToAddress([]byte{0x22})},
+ {Address: common.BytesToAddress([]byte{0x02, 0x22})},
+ },
+ TxHash: tx2.Hash(),
+ ContractAddress: common.BytesToAddress([]byte{0x02, 0x22, 0x22}),
+ GasUsed: 222222,
+ }
+ receipt2.Bloom = types.CreateBloom(types.Receipts{receipt2})
+ receipts := []*types.Receipt{receipt1, receipt2}
+
+ hash := common.BytesToHash([]byte{0x03, 0x14})
+ // Check that no receipt entries are in a pristine database
+ if rs := ReadReceipts(db, hash, 0, params.TestChainConfig); len(rs) != 0 {
+ t.Fatalf("non existent receipts returned: %v", rs)
+ }
+ // Insert the body that corresponds to the receipts
+ WriteBody(db, hash, 0, body)
+
+ // Insert the receipt slice into the database and check presence
+ WriteReceipts(db, hash, 0, receipts)
+
+ logs := ReadLogs(db, hash, 0)
+ if len(logs) == 0 {
+ t.Fatalf("no logs returned")
+ }
+ if have, want := len(logs), 2; have != want {
+ t.Fatalf("unexpected number of logs returned, have %d want %d", have, want)
+ }
+ if have, want := len(logs[0]), 2; have != want {
+ t.Fatalf("unexpected number of logs[0] returned, have %d want %d", have, want)
+ }
+ if have, want := len(logs[1]), 2; have != want {
+ t.Fatalf("unexpected number of logs[1] returned, have %d want %d", have, want)
+ }
+
+ // Fill in log fields so we can compare their rlp encoding
+ if err := types.Receipts(receipts).DeriveFields(params.TestChainConfig, hash, 0, body.Transactions); err != nil {
+ t.Fatal(err)
+ }
+ for i, pr := range receipts {
+ for j, pl := range pr.Logs {
+ rlpHave, err := rlp.EncodeToBytes(newFullLogRLP(logs[i][j]))
+ if err != nil {
+ t.Fatal(err)
+ }
+ rlpWant, err := rlp.EncodeToBytes(newFullLogRLP(pl))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if !bytes.Equal(rlpHave, rlpWant) {
+ t.Fatalf("receipt #%d: receipt mismatch: have %s, want %s", i, hex.EncodeToString(rlpHave), hex.EncodeToString(rlpWant))
+ }
+ }
+ }
+}
+
+func TestDeriveLogFields(t *testing.T) {
+ // Create a few transactions to have receipts for
+ to2 := common.HexToAddress("0x2")
+ to3 := common.HexToAddress("0x3")
+ txs := types.Transactions{
+ types.NewTx(&types.LegacyTx{
+ Nonce: 1,
+ Value: big.NewInt(1),
+ Gas: 1,
+ GasPrice: big.NewInt(1),
+ }),
+ types.NewTx(&types.LegacyTx{
+ To: &to2,
+ Nonce: 2,
+ Value: big.NewInt(2),
+ Gas: 2,
+ GasPrice: big.NewInt(2),
+ }),
+ types.NewTx(&types.AccessListTx{
+ To: &to3,
+ Nonce: 3,
+ Value: big.NewInt(3),
+ Gas: 3,
+ GasPrice: big.NewInt(3),
+ }),
+ }
+ // Create the corresponding receipts
+ receipts := []*receiptLogs{
+ {
+ Logs: []*types.Log{
+ {Address: common.BytesToAddress([]byte{0x11})},
+ {Address: common.BytesToAddress([]byte{0x01, 0x11})},
+ },
+ },
+ {
+ Logs: []*types.Log{
+ {Address: common.BytesToAddress([]byte{0x22})},
+ {Address: common.BytesToAddress([]byte{0x02, 0x22})},
+ },
+ },
+ {
+ Logs: []*types.Log{
+ {Address: common.BytesToAddress([]byte{0x33})},
+ {Address: common.BytesToAddress([]byte{0x03, 0x33})},
+ },
+ },
+ }
+
+ // Derive log metadata fields
+ number := big.NewInt(1)
+ hash := common.BytesToHash([]byte{0x03, 0x14})
+ if err := deriveLogFields(receipts, hash, number.Uint64(), txs); err != nil {
+ t.Fatal(err)
+ }
+
+ // Iterate over all the computed fields and check that they're correct
+ logIndex := uint(0)
+ for i := range receipts {
+ 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++
+ }
+ }
+}
+
+func BenchmarkDecodeRLPLogs(b *testing.B) {
+ // Encoded receipts from block 0x14ee094309fbe8f70b65f45ebcc08fb33f126942d97464aad5eb91cfd1e2d269
+ buf, err := ioutil.ReadFile("testdata/stored_receipts.bin")
+ if err != nil {
+ b.Fatal(err)
+ }
+ b.Run("ReceiptForStorage", func(b *testing.B) {
+ b.ReportAllocs()
+ var r []*types.ReceiptForStorage
+ for i := 0; i < b.N; i++ {
+ if err := rlp.DecodeBytes(buf, &r); err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+ b.Run("rlpLogs", func(b *testing.B) {
+ b.ReportAllocs()
+ var r []*receiptLogs
+ for i := 0; i < b.N; i++ {
+ if err := rlp.DecodeBytes(buf, &r); err != nil {
+ b.Fatal(err)
+ }
+ }
+ })
+}
diff --git a/core/rawdb/database.go b/core/rawdb/database.go
index 90619169a0..f7c7f5d336 100644
--- a/core/rawdb/database.go
+++ b/core/rawdb/database.go
@@ -89,8 +89,8 @@ func (db *nofreezedb) Ancient(kind string, number uint64) ([]byte, error) {
return nil, errNotSupported
}
-// ReadAncients returns an error as we don't have a backing chain freezer.
-func (db *nofreezedb) ReadAncients(kind string, start, max, maxByteSize uint64) ([][]byte, error) {
+// AncientRange returns an error as we don't have a backing chain freezer.
+func (db *nofreezedb) AncientRange(kind string, start, max, maxByteSize uint64) ([][]byte, error) {
return nil, errNotSupported
}
@@ -104,9 +104,9 @@ func (db *nofreezedb) AncientSize(kind string) (uint64, error) {
return 0, errNotSupported
}
-// AppendAncient returns an error as we don't have a backing chain freezer.
-func (db *nofreezedb) AppendAncient(number uint64, hash, header, body, receipts, td []byte) error {
- return errNotSupported
+// ModifyAncients is not supported.
+func (db *nofreezedb) ModifyAncients(func(ethdb.AncientWriteOp) error) (int64, error) {
+ return 0, errNotSupported
}
// TruncateAncients returns an error as we don't have a backing chain freezer.
@@ -119,12 +119,26 @@ func (db *nofreezedb) Sync() error {
return errNotSupported
}
+func (db *nofreezedb) ReadAncients(fn func(reader ethdb.AncientReader) error) (err error) {
+ // Unlike other ancient-related methods, this method does not return
+ // errNotSupported when invoked.
+ // The reason for this is that the caller might want to do several things:
+ // 1. Check if something is in freezer,
+ // 2. If not, check leveldb.
+ //
+ // This will work, since the ancient-checks inside 'fn' will return errors,
+ // and the leveldb work will continue.
+ //
+ // If we instead were to return errNotSupported here, then the caller would
+ // have to explicitly check for that, having an extra clause to do the
+ // non-ancient operations.
+ return fn(db)
+}
+
// NewDatabase creates a high level database on top of a given key-value data
// store without a freezer moving immutable chain segments into cold storage.
func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {
- return &nofreezedb{
- KeyValueStore: db,
- }
+ return &nofreezedb{KeyValueStore: db}
}
// NewDatabaseWithFreezer creates a high level database on top of a given key-
@@ -132,7 +146,7 @@ func NewDatabase(db ethdb.KeyValueStore) ethdb.Database {
// storage.
func NewDatabaseWithFreezer(db ethdb.KeyValueStore, freezer string, namespace string, readonly bool) (ethdb.Database, error) {
// Create the idle freezer instance
- frdb, err := newFreezer(freezer, namespace, readonly)
+ frdb, err := newFreezer(freezer, namespace, readonly, freezerTableSize, FreezerNoSnappy)
if err != nil {
return nil, err
}
diff --git a/core/rawdb/freezer.go b/core/rawdb/freezer.go
index 4262a615a7..e19c202adc 100644
--- a/core/rawdb/freezer.go
+++ b/core/rawdb/freezer.go
@@ -61,6 +61,9 @@ const (
// freezerBatchLimit is the maximum number of blocks to freeze in one batch
// before doing an fsync and deleting it from the key-value store.
freezerBatchLimit = 30000
+
+ // freezerTableSize defines the maximum size of freezer data files.
+ freezerTableSize = 2 * 1000 * 1000 * 1000
)
// freezer is an memory mapped append-only database to store immutable chain data
@@ -77,6 +80,11 @@ type freezer struct {
frozen uint64 // Number of blocks already frozen
threshold uint64 // Number of recent blocks not to freeze (params.FullImmutabilityThreshold apart from tests)
+ // This lock synchronizes writers and the truncate operation, as well as
+ // the "atomic" (batched) read operations.
+ writeLock sync.RWMutex
+ writeBatch *freezerBatch
+
readonly bool
tables map[string]*freezerTable // Data tables for storing everything
instanceLock fileutil.Releaser // File-system lock to prevent double opens
@@ -90,7 +98,10 @@ type freezer struct {
// newFreezer creates a chain freezer that moves ancient chain data into
// append-only flat file containers.
-func newFreezer(datadir string, namespace string, readonly bool) (*freezer, error) {
+//
+// The 'tables' argument defines the data tables. If the value of a map
+// entry is true, snappy compression is disabled for the table.
+func newFreezer(datadir string, namespace string, readonly bool, maxTableSize uint32, tables map[string]bool) (*freezer, error) {
// Create the initial freezer object
var (
readMeter = metrics.NewRegisteredMeter(namespace+"ancient/read", nil)
@@ -119,8 +130,10 @@ func newFreezer(datadir string, namespace string, readonly bool) (*freezer, erro
trigger: make(chan chan struct{}),
quit: make(chan struct{}),
}
- for name, disableSnappy := range FreezerNoSnappy {
- table, err := newTable(datadir, name, readMeter, writeMeter, sizeGauge, disableSnappy)
+
+ // Create the tables.
+ for name, disableSnappy := range tables {
+ table, err := newTable(datadir, name, readMeter, writeMeter, sizeGauge, maxTableSize, disableSnappy)
if err != nil {
for _, table := range freezer.tables {
table.Close()
@@ -130,6 +143,8 @@ func newFreezer(datadir string, namespace string, readonly bool) (*freezer, erro
}
freezer.tables[name] = table
}
+
+ // Truncate all tables to common length.
if err := freezer.repair(); err != nil {
for _, table := range freezer.tables {
table.Close()
@@ -137,12 +152,19 @@ func newFreezer(datadir string, namespace string, readonly bool) (*freezer, erro
lock.Release()
return nil, err
}
+
+ // Create the write batch.
+ freezer.writeBatch = newFreezerBatch(freezer)
+
log.Info("Opened ancient database", "database", datadir, "readonly", readonly)
return freezer, nil
}
// Close terminates the chain freezer, unmapping all the data files.
func (f *freezer) Close() error {
+ f.writeLock.Lock()
+ defer f.writeLock.Unlock()
+
var errs []error
f.closeOnce.Do(func() {
close(f.quit)
@@ -180,12 +202,12 @@ func (f *freezer) Ancient(kind string, number uint64) ([]byte, error) {
return nil, errUnknownTable
}
-// ReadAncients retrieves multiple items in sequence, starting from the index 'start'.
+// AncientRange retrieves multiple items in sequence, starting from the index 'start'.
// It will return
// - at most 'max' items,
// - at least 1 item (even if exceeding the maxByteSize), but will otherwise
// return as many items as fit into maxByteSize.
-func (f *freezer) ReadAncients(kind string, start, count, maxBytes uint64) ([][]byte, error) {
+func (f *freezer) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) {
if table := f.tables[kind]; table != nil {
return table.RetrieveItems(start, count, maxBytes)
}
@@ -199,60 +221,57 @@ func (f *freezer) Ancients() (uint64, error) {
// AncientSize returns the ancient size of the specified category.
func (f *freezer) AncientSize(kind string) (uint64, error) {
+ // This needs the write lock to avoid data races on table fields.
+ // Speed doesn't matter here, AncientSize is for debugging.
+ f.writeLock.RLock()
+ defer f.writeLock.RUnlock()
+
if table := f.tables[kind]; table != nil {
return table.size()
}
return 0, errUnknownTable
}
-// AppendAncient injects all binary blobs belong to block at the end of the
-// append-only immutable table files.
-//
-// Notably, this function is lock free but kind of thread-safe. All out-of-order
-// injection will be rejected. But if two injections with same number happen at
-// the same time, we can get into the trouble.
-func (f *freezer) AppendAncient(number uint64, hash, header, body, receipts, td []byte) (err error) {
+// ReadAncients runs the given read operation while ensuring that no writes take place
+// on the underlying freezer.
+func (f *freezer) ReadAncients(fn func(ethdb.AncientReader) error) (err error) {
+ f.writeLock.RLock()
+ defer f.writeLock.RUnlock()
+ return fn(f)
+}
+
+// ModifyAncients runs the given write operation.
+func (f *freezer) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (writeSize int64, err error) {
if f.readonly {
- return errReadOnly
+ return 0, errReadOnly
}
- // Ensure the binary blobs we are appending is continuous with freezer.
- if atomic.LoadUint64(&f.frozen) != number {
- return errOutOrderInsertion
- }
- // Rollback all inserted data if any insertion below failed to ensure
- // the tables won't out of sync.
+ f.writeLock.Lock()
+ defer f.writeLock.Unlock()
+
+ // Roll back all tables to the starting position in case of error.
+ prevItem := f.frozen
defer func() {
if err != nil {
- rerr := f.repair()
- if rerr != nil {
- log.Crit("Failed to repair freezer", "err", rerr)
+ // The write operation has failed. Go back to the previous item position.
+ for name, table := range f.tables {
+ err := table.truncate(prevItem)
+ if err != nil {
+ log.Error("Freezer table roll-back failed", "table", name, "index", prevItem, "err", err)
+ }
}
- log.Info("Append ancient failed", "number", number, "err", err)
}
}()
- // Inject all the components into the relevant data tables
- if err := f.tables[freezerHashTable].Append(f.frozen, hash[:]); err != nil {
- log.Error("Failed to append ancient hash", "number", f.frozen, "hash", hash, "err", err)
- return err
+
+ f.writeBatch.reset()
+ if err := fn(f.writeBatch); err != nil {
+ return 0, err
}
- if err := f.tables[freezerHeaderTable].Append(f.frozen, header); err != nil {
- log.Error("Failed to append ancient header", "number", f.frozen, "hash", hash, "err", err)
- return err
+ item, writeSize, err := f.writeBatch.commit()
+ if err != nil {
+ return 0, err
}
- if err := f.tables[freezerBodiesTable].Append(f.frozen, body); err != nil {
- log.Error("Failed to append ancient body", "number", f.frozen, "hash", hash, "err", err)
- return err
- }
- if err := f.tables[freezerReceiptTable].Append(f.frozen, receipts); err != nil {
- log.Error("Failed to append ancient receipts", "number", f.frozen, "hash", hash, "err", err)
- return err
- }
- if err := f.tables[freezerDifficultyTable].Append(f.frozen, td); err != nil {
- log.Error("Failed to append ancient difficulty", "number", f.frozen, "hash", hash, "err", err)
- return err
- }
- atomic.AddUint64(&f.frozen, 1) // Only modify atomically
- return nil
+ atomic.StoreUint64(&f.frozen, item)
+ return writeSize, nil
}
// TruncateAncients discards any recent data above the provided threshold number.
@@ -260,6 +279,9 @@ func (f *freezer) TruncateAncients(items uint64) error {
if f.readonly {
return errReadOnly
}
+ f.writeLock.Lock()
+ defer f.writeLock.Unlock()
+
if atomic.LoadUint64(&f.frozen) <= items {
return nil
}
@@ -286,6 +308,24 @@ func (f *freezer) Sync() error {
return nil
}
+// repair truncates all data tables to the same length.
+func (f *freezer) repair() error {
+ min := uint64(math.MaxUint64)
+ for _, table := range f.tables {
+ items := atomic.LoadUint64(&table.items)
+ if min > items {
+ min = items
+ }
+ }
+ for _, table := range f.tables {
+ if err := table.truncate(min); err != nil {
+ return err
+ }
+ }
+ atomic.StoreUint64(&f.frozen, min)
+ return nil
+}
+
// freeze is a background thread that periodically checks the blockchain for any
// import progress and moves ancient data from the fast database into the freezer.
//
@@ -352,54 +392,28 @@ func (f *freezer) freeze(db ethdb.KeyValueStore) {
backoff = true
continue
}
+
// Seems we have data ready to be frozen, process in usable batches
- limit := *number - threshold
- if limit-f.frozen > freezerBatchLimit {
- limit = f.frozen + freezerBatchLimit
- }
var (
start = time.Now()
- first = f.frozen
- ancients = make([]common.Hash, 0, limit-f.frozen)
+ first, _ = f.Ancients()
+ limit = *number - threshold
)
- for f.frozen <= limit {
- // Retrieves all the components of the canonical block
- hash := ReadCanonicalHash(nfdb, f.frozen)
- if hash == (common.Hash{}) {
- log.Error("Canonical hash missing, can't freeze", "number", f.frozen)
- break
- }
- header := ReadHeaderRLP(nfdb, hash, f.frozen)
- if len(header) == 0 {
- log.Error("Block header missing, can't freeze", "number", f.frozen, "hash", hash)
- break
- }
- body := ReadBodyRLP(nfdb, hash, f.frozen)
- if len(body) == 0 {
- log.Error("Block body missing, can't freeze", "number", f.frozen, "hash", hash)
- break
- }
- receipts := ReadReceiptsRLP(nfdb, hash, f.frozen)
- if len(receipts) == 0 {
- log.Error("Block receipts missing, can't freeze", "number", f.frozen, "hash", hash)
- break
- }
- td := ReadTdRLP(nfdb, hash, f.frozen)
- if len(td) == 0 {
- log.Error("Total difficulty missing, can't freeze", "number", f.frozen, "hash", hash)
- break
- }
- log.Trace("Deep froze ancient block", "number", f.frozen, "hash", hash)
- // Inject all the components into the relevant data tables
- if err := f.AppendAncient(f.frozen, hash[:], header, body, receipts, td); err != nil {
- break
- }
- ancients = append(ancients, hash)
+ if limit-first > freezerBatchLimit {
+ limit = first + freezerBatchLimit
}
+ ancients, err := f.freezeRange(nfdb, first, limit)
+ if err != nil {
+ log.Error("Error in block freeze operation", "err", err)
+ backoff = true
+ continue
+ }
+
// Batch of blocks have been frozen, flush them before wiping from leveldb
if err := f.Sync(); err != nil {
log.Crit("Failed to flush frozen tables", "err", err)
}
+
// Wipe out all data from the active database
batch := db.NewBatch()
for i := 0; i < len(ancients); i++ {
@@ -464,6 +478,7 @@ func (f *freezer) freeze(db ethdb.KeyValueStore) {
log.Crit("Failed to delete dangling side blocks", "err", err)
}
}
+
// Log something friendly for the user
context := []interface{}{
"blocks", f.frozen - first, "elapsed", common.PrettyDuration(time.Since(start)), "number", f.frozen - 1,
@@ -480,20 +495,54 @@ func (f *freezer) freeze(db ethdb.KeyValueStore) {
}
}
-// repair truncates all data tables to the same length.
-func (f *freezer) repair() error {
- min := uint64(math.MaxUint64)
- for _, table := range f.tables {
- items := atomic.LoadUint64(&table.items)
- if min > items {
- min = items
+func (f *freezer) freezeRange(nfdb *nofreezedb, number, limit uint64) (hashes []common.Hash, err error) {
+ hashes = make([]common.Hash, 0, limit-number)
+
+ _, err = f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
+ for ; number <= limit; number++ {
+ // Retrieve all the components of the canonical block.
+ hash := ReadCanonicalHash(nfdb, number)
+ if hash == (common.Hash{}) {
+ return fmt.Errorf("canonical hash missing, can't freeze block %d", number)
+ }
+ header := ReadHeaderRLP(nfdb, hash, number)
+ if len(header) == 0 {
+ return fmt.Errorf("block header missing, can't freeze block %d", number)
+ }
+ body := ReadBodyRLP(nfdb, hash, number)
+ if len(body) == 0 {
+ return fmt.Errorf("block body missing, can't freeze block %d", number)
+ }
+ receipts := ReadReceiptsRLP(nfdb, hash, number)
+ if len(receipts) == 0 {
+ return fmt.Errorf("block receipts missing, can't freeze block %d", number)
+ }
+ td := ReadTdRLP(nfdb, hash, number)
+ if len(td) == 0 {
+ return fmt.Errorf("total difficulty missing, can't freeze block %d", number)
+ }
+
+ // Write to the batch.
+ if err := op.AppendRaw(freezerHashTable, number, hash[:]); err != nil {
+ return fmt.Errorf("can't write hash to freezer: %v", err)
+ }
+ if err := op.AppendRaw(freezerHeaderTable, number, header); err != nil {
+ return fmt.Errorf("can't write header to freezer: %v", err)
+ }
+ if err := op.AppendRaw(freezerBodiesTable, number, body); err != nil {
+ return fmt.Errorf("can't write body to freezer: %v", err)
+ }
+ if err := op.AppendRaw(freezerReceiptTable, number, receipts); err != nil {
+ return fmt.Errorf("can't write receipts to freezer: %v", err)
+ }
+ if err := op.AppendRaw(freezerDifficultyTable, number, td); err != nil {
+ return fmt.Errorf("can't write td to freezer: %v", err)
+ }
+
+ hashes = append(hashes, hash)
}
- }
- for _, table := range f.tables {
- if err := table.truncate(min); err != nil {
- return err
- }
- }
- atomic.StoreUint64(&f.frozen, min)
- return nil
+ return nil
+ })
+
+ return hashes, err
}
diff --git a/core/rawdb/freezer_batch.go b/core/rawdb/freezer_batch.go
new file mode 100644
index 0000000000..8297c0ac1d
--- /dev/null
+++ b/core/rawdb/freezer_batch.go
@@ -0,0 +1,248 @@
+// 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 rawdb
+
+import (
+ "fmt"
+ "sync/atomic"
+
+ "github.com/ethereum/go-ethereum/common/math"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/golang/snappy"
+)
+
+// This is the maximum amount of data that will be buffered in memory
+// for a single freezer table batch.
+const freezerBatchBufferLimit = 2 * 1024 * 1024
+
+// freezerBatch is a write operation of multiple items on a freezer.
+type freezerBatch struct {
+ tables map[string]*freezerTableBatch
+}
+
+func newFreezerBatch(f *freezer) *freezerBatch {
+ batch := &freezerBatch{tables: make(map[string]*freezerTableBatch, len(f.tables))}
+ for kind, table := range f.tables {
+ batch.tables[kind] = table.newBatch()
+ }
+ return batch
+}
+
+// Append adds an RLP-encoded item of the given kind.
+func (batch *freezerBatch) Append(kind string, num uint64, item interface{}) error {
+ return batch.tables[kind].Append(num, item)
+}
+
+// AppendRaw adds an item of the given kind.
+func (batch *freezerBatch) AppendRaw(kind string, num uint64, item []byte) error {
+ return batch.tables[kind].AppendRaw(num, item)
+}
+
+// reset initializes the batch.
+func (batch *freezerBatch) reset() {
+ for _, tb := range batch.tables {
+ tb.reset()
+ }
+}
+
+// commit is called at the end of a write operation and
+// writes all remaining data to tables.
+func (batch *freezerBatch) commit() (item uint64, writeSize int64, err error) {
+ // Check that count agrees on all batches.
+ item = uint64(math.MaxUint64)
+ for name, tb := range batch.tables {
+ if item < math.MaxUint64 && tb.curItem != item {
+ return 0, 0, fmt.Errorf("table %s is at item %d, want %d", name, tb.curItem, item)
+ }
+ item = tb.curItem
+ }
+
+ // Commit all table batches.
+ for _, tb := range batch.tables {
+ if err := tb.commit(); err != nil {
+ return 0, 0, err
+ }
+ writeSize += tb.totalBytes
+ }
+ return item, writeSize, nil
+}
+
+// freezerTableBatch is a batch for a freezer table.
+type freezerTableBatch struct {
+ t *freezerTable
+
+ sb *snappyBuffer
+ encBuffer writeBuffer
+ dataBuffer []byte
+ indexBuffer []byte
+ curItem uint64 // expected index of next append
+ totalBytes int64 // counts written bytes since reset
+}
+
+// newBatch creates a new batch for the freezer table.
+func (t *freezerTable) newBatch() *freezerTableBatch {
+ batch := &freezerTableBatch{t: t}
+ if !t.noCompression {
+ batch.sb = new(snappyBuffer)
+ }
+ batch.reset()
+ return batch
+}
+
+// reset clears the batch for reuse.
+func (batch *freezerTableBatch) reset() {
+ batch.dataBuffer = batch.dataBuffer[:0]
+ batch.indexBuffer = batch.indexBuffer[:0]
+ batch.curItem = atomic.LoadUint64(&batch.t.items)
+ batch.totalBytes = 0
+}
+
+// Append rlp-encodes and adds data at the end of the freezer table. The item number is a
+// precautionary parameter to ensure data correctness, but the table will reject already
+// existing data.
+func (batch *freezerTableBatch) Append(item uint64, data interface{}) error {
+ if item != batch.curItem {
+ return errOutOrderInsertion
+ }
+
+ // Encode the item.
+ batch.encBuffer.Reset()
+ if err := rlp.Encode(&batch.encBuffer, data); err != nil {
+ return err
+ }
+ encItem := batch.encBuffer.data
+ if batch.sb != nil {
+ encItem = batch.sb.compress(encItem)
+ }
+ return batch.appendItem(encItem)
+}
+
+// AppendRaw injects a binary blob at the end of the freezer table. The item number is a
+// precautionary parameter to ensure data correctness, but the table will reject already
+// existing data.
+func (batch *freezerTableBatch) AppendRaw(item uint64, blob []byte) error {
+ if item != batch.curItem {
+ return errOutOrderInsertion
+ }
+
+ encItem := blob
+ if batch.sb != nil {
+ encItem = batch.sb.compress(blob)
+ }
+ return batch.appendItem(encItem)
+}
+
+func (batch *freezerTableBatch) appendItem(data []byte) error {
+ // Check if item fits into current data file.
+ itemSize := int64(len(data))
+ itemOffset := batch.t.headBytes + int64(len(batch.dataBuffer))
+ if itemOffset+itemSize > int64(batch.t.maxFileSize) {
+ // It doesn't fit, go to next file first.
+ if err := batch.commit(); err != nil {
+ return err
+ }
+ if err := batch.t.advanceHead(); err != nil {
+ return err
+ }
+ itemOffset = 0
+ }
+
+ // Put data to buffer.
+ batch.dataBuffer = append(batch.dataBuffer, data...)
+ batch.totalBytes += itemSize
+
+ // Put index entry to buffer.
+ entry := indexEntry{filenum: batch.t.headId, offset: uint32(itemOffset + itemSize)}
+ batch.indexBuffer = entry.append(batch.indexBuffer)
+ batch.curItem++
+
+ return batch.maybeCommit()
+}
+
+// maybeCommit writes the buffered data if the buffer is full enough.
+func (batch *freezerTableBatch) maybeCommit() error {
+ if len(batch.dataBuffer) > freezerBatchBufferLimit {
+ return batch.commit()
+ }
+ return nil
+}
+
+// commit writes the batched items to the backing freezerTable.
+func (batch *freezerTableBatch) commit() error {
+ // Write data.
+ _, err := batch.t.head.Write(batch.dataBuffer)
+ if err != nil {
+ return err
+ }
+ dataSize := int64(len(batch.dataBuffer))
+ batch.dataBuffer = batch.dataBuffer[:0]
+
+ // Write index.
+ _, err = batch.t.index.Write(batch.indexBuffer)
+ if err != nil {
+ return err
+ }
+ indexSize := int64(len(batch.indexBuffer))
+ batch.indexBuffer = batch.indexBuffer[:0]
+
+ // Update headBytes of table.
+ batch.t.headBytes += dataSize
+ atomic.StoreUint64(&batch.t.items, batch.curItem)
+
+ // Update metrics.
+ batch.t.sizeGauge.Inc(dataSize + indexSize)
+ batch.t.writeMeter.Mark(dataSize + indexSize)
+ return nil
+}
+
+// snappyBuffer writes snappy in block format, and can be reused. It is
+// reset when WriteTo is called.
+type snappyBuffer struct {
+ dst []byte
+}
+
+// compress snappy-compresses the data.
+func (s *snappyBuffer) compress(data []byte) []byte {
+ // The snappy library does not care what the capacity of the buffer is,
+ // but only checks the length. If the length is too small, it will
+ // allocate a brand new buffer.
+ // To avoid that, we check the required size here, and grow the size of the
+ // buffer to utilize the full capacity.
+ if n := snappy.MaxEncodedLen(len(data)); len(s.dst) < n {
+ if cap(s.dst) < n {
+ s.dst = make([]byte, n)
+ }
+ s.dst = s.dst[:n]
+ }
+
+ s.dst = snappy.Encode(s.dst, data)
+ return s.dst
+}
+
+// writeBuffer implements io.Writer for a byte slice.
+type writeBuffer struct {
+ data []byte
+}
+
+func (wb *writeBuffer) Write(data []byte) (int, error) {
+ wb.data = append(wb.data, data...)
+ return len(data), nil
+}
+
+func (wb *writeBuffer) Reset() {
+ wb.data = wb.data[:0]
+}
diff --git a/core/rawdb/freezer_table.go b/core/rawdb/freezer_table.go
index 9d052f7cd8..22405cf9b4 100644
--- a/core/rawdb/freezer_table.go
+++ b/core/rawdb/freezer_table.go
@@ -17,6 +17,7 @@
package rawdb
import (
+ "bytes"
"encoding/binary"
"errors"
"fmt"
@@ -55,19 +56,20 @@ type indexEntry struct {
const indexEntrySize = 6
-// unmarshallBinary deserializes binary b into the rawIndex entry.
+// unmarshalBinary deserializes binary b into the rawIndex entry.
func (i *indexEntry) unmarshalBinary(b []byte) error {
i.filenum = uint32(binary.BigEndian.Uint16(b[:2]))
i.offset = binary.BigEndian.Uint32(b[2:6])
return nil
}
-// marshallBinary serializes the rawIndex entry into binary.
-func (i *indexEntry) marshallBinary() []byte {
- b := make([]byte, indexEntrySize)
- binary.BigEndian.PutUint16(b[:2], uint16(i.filenum))
- binary.BigEndian.PutUint32(b[2:6], i.offset)
- return b
+// append adds the encoded entry to the end of b.
+func (i *indexEntry) append(b []byte) []byte {
+ offset := len(b)
+ out := append(b, make([]byte, indexEntrySize)...)
+ binary.BigEndian.PutUint16(out[offset:], uint16(i.filenum))
+ binary.BigEndian.PutUint32(out[offset+2:], i.offset)
+ return out
}
// bounds returns the start- and end- offsets, and the file number of where to
@@ -107,7 +109,7 @@ type freezerTable struct {
// to count how many historic items have gone missing.
itemOffset uint32 // Offset (number of discarded items)
- headBytes uint32 // Number of bytes written to the head file
+ headBytes int64 // Number of bytes written to the head file
readMeter metrics.Meter // Meter for measuring the effective amount of data read
writeMeter metrics.Meter // Meter for measuring the effective amount of data written
sizeGauge metrics.Gauge // Gauge for tracking the combined size of all freezer tables
@@ -118,12 +120,7 @@ type freezerTable struct {
// NewFreezerTable opens the given path as a freezer table.
func NewFreezerTable(path, name string, disableSnappy bool) (*freezerTable, error) {
- return newTable(path, name, metrics.NilMeter{}, metrics.NilMeter{}, metrics.NilGauge{}, disableSnappy)
-}
-
-// newTable opens a freezer table with default settings - 2G files
-func newTable(path string, name string, readMeter metrics.Meter, writeMeter metrics.Meter, sizeGauge metrics.Gauge, disableSnappy bool) (*freezerTable, error) {
- return newCustomTable(path, name, readMeter, writeMeter, sizeGauge, 2*1000*1000*1000, disableSnappy)
+ return newTable(path, name, metrics.NilMeter{}, metrics.NilMeter{}, metrics.NilGauge{}, freezerTableSize, disableSnappy)
}
// openFreezerFileForAppend opens a freezer table file and seeks to the end
@@ -164,10 +161,10 @@ func truncateFreezerFile(file *os.File, size int64) error {
return nil
}
-// newCustomTable opens a freezer table, creating the data and index files if they are
+// newTable opens a freezer table, creating the data and index files if they are
// non existent. Both files are truncated to the shortest common length to ensure
// they don't go out of sync.
-func newCustomTable(path string, name string, readMeter metrics.Meter, writeMeter metrics.Meter, sizeGauge metrics.Gauge, maxFilesize uint32, noCompression bool) (*freezerTable, error) {
+func newTable(path string, name string, readMeter metrics.Meter, writeMeter metrics.Meter, sizeGauge metrics.Gauge, maxFilesize uint32, noCompression bool) (*freezerTable, error) {
// Ensure the containing directory exists and open the indexEntry file
if err := os.MkdirAll(path, 0755); err != nil {
return nil, err
@@ -313,7 +310,7 @@ func (t *freezerTable) repair() error {
}
// Update the item and byte counters and return
t.items = uint64(t.itemOffset) + uint64(offsetsSize/indexEntrySize-1) // last indexEntry points to the end of the data file
- t.headBytes = uint32(contentSize)
+ t.headBytes = contentSize
t.headId = lastIndex.filenum
// Close opened files and preopen all files
@@ -387,14 +384,14 @@ func (t *freezerTable) truncate(items uint64) error {
t.releaseFilesAfter(expected.filenum, true)
// Set back the historic head
t.head = newHead
- atomic.StoreUint32(&t.headId, expected.filenum)
+ t.headId = expected.filenum
}
if err := truncateFreezerFile(t.head, int64(expected.offset)); err != nil {
return err
}
// All data files truncated, set internal counters and return
+ t.headBytes = int64(expected.offset)
atomic.StoreUint64(&t.items, items)
- atomic.StoreUint32(&t.headBytes, expected.offset)
// Retrieve the new size and update the total size counter
newSize, err := t.sizeNolock()
@@ -471,94 +468,6 @@ func (t *freezerTable) releaseFilesAfter(num uint32, remove bool) {
}
}
-// Append injects a binary blob at the end of the freezer table. The item number
-// is a precautionary parameter to ensure data correctness, but the table will
-// reject already existing data.
-//
-// Note, this method will *not* flush any data to disk so be sure to explicitly
-// fsync before irreversibly deleting data from the database.
-func (t *freezerTable) Append(item uint64, blob []byte) error {
- // Encode the blob before the lock portion
- if !t.noCompression {
- blob = snappy.Encode(nil, blob)
- }
- // Read lock prevents competition with truncate
- retry, err := t.append(item, blob, false)
- if err != nil {
- return err
- }
- if retry {
- // Read lock was insufficient, retry with a writelock
- _, err = t.append(item, blob, true)
- }
- return err
-}
-
-// append injects a binary blob at the end of the freezer table.
-// Normally, inserts do not require holding the write-lock, so it should be invoked with 'wlock' set to
-// false.
-// However, if the data will grown the current file out of bounds, then this
-// method will return 'true, nil', indicating that the caller should retry, this time
-// with 'wlock' set to true.
-func (t *freezerTable) append(item uint64, encodedBlob []byte, wlock bool) (bool, error) {
- if wlock {
- t.lock.Lock()
- defer t.lock.Unlock()
- } else {
- t.lock.RLock()
- defer t.lock.RUnlock()
- }
- // Ensure the table is still accessible
- if t.index == nil || t.head == nil {
- return false, errClosed
- }
- // Ensure only the next item can be written, nothing else
- if atomic.LoadUint64(&t.items) != item {
- return false, fmt.Errorf("appending unexpected item: want %d, have %d", t.items, item)
- }
- bLen := uint32(len(encodedBlob))
- if t.headBytes+bLen < bLen ||
- t.headBytes+bLen > t.maxFileSize {
- // Writing would overflow, so we need to open a new data file.
- // If we don't already hold the writelock, abort and let the caller
- // invoke this method a second time.
- if !wlock {
- return true, nil
- }
- nextID := atomic.LoadUint32(&t.headId) + 1
- // We open the next file in truncated mode -- if this file already
- // exists, we need to start over from scratch on it
- newHead, err := t.openFile(nextID, openFreezerFileTruncated)
- if err != nil {
- return false, err
- }
- // Close old file, and reopen in RDONLY mode
- t.releaseFile(t.headId)
- t.openFile(t.headId, openFreezerFileForReadOnly)
-
- // Swap out the current head
- t.head = newHead
- atomic.StoreUint32(&t.headBytes, 0)
- atomic.StoreUint32(&t.headId, nextID)
- }
- if _, err := t.head.Write(encodedBlob); err != nil {
- return false, err
- }
- newOffset := atomic.AddUint32(&t.headBytes, bLen)
- idx := indexEntry{
- filenum: atomic.LoadUint32(&t.headId),
- offset: newOffset,
- }
- // Write indexEntry
- t.index.Write(idx.marshallBinary())
-
- t.writeMeter.Mark(int64(bLen + indexEntrySize))
- t.sizeGauge.Inc(int64(bLen + indexEntrySize))
-
- atomic.AddUint64(&t.items, 1)
- return false, nil
-}
-
// getIndices returns the index entries for the given from-item, covering 'count' items.
// N.B: The actual number of returned indices for N items will always be N+1 (unless an
// error is returned).
@@ -651,6 +560,7 @@ func (t *freezerTable) RetrieveItems(start, count, maxBytes uint64) ([][]byte, e
func (t *freezerTable) retrieveItems(start, count, maxBytes uint64) ([]byte, []int, error) {
t.lock.RLock()
defer t.lock.RUnlock()
+
// Ensure the table and the item is accessible
if t.index == nil || t.head == nil {
return nil, nil, errClosed
@@ -763,6 +673,32 @@ func (t *freezerTable) sizeNolock() (uint64, error) {
return total, nil
}
+// advanceHead should be called when the current head file would outgrow the file limits,
+// and a new file must be opened. The caller of this method must hold the write-lock
+// before calling this method.
+func (t *freezerTable) advanceHead() error {
+ t.lock.Lock()
+ defer t.lock.Unlock()
+
+ // We open the next file in truncated mode -- if this file already
+ // exists, we need to start over from scratch on it.
+ nextID := t.headId + 1
+ newHead, err := t.openFile(nextID, openFreezerFileTruncated)
+ if err != nil {
+ return err
+ }
+
+ // Close old file, and reopen in RDONLY mode.
+ t.releaseFile(t.headId)
+ t.openFile(t.headId, openFreezerFileForReadOnly)
+
+ // Swap out the current head.
+ t.head = newHead
+ t.headBytes = 0
+ t.headId = nextID
+ return nil
+}
+
// Sync pushes any pending data from memory out to disk. This is an expensive
// operation, so use it with care.
func (t *freezerTable) Sync() error {
@@ -775,10 +711,21 @@ func (t *freezerTable) Sync() error {
// DumpIndex is a debug print utility function, mainly for testing. It can also
// be used to analyse a live freezer table index.
func (t *freezerTable) DumpIndex(start, stop int64) {
+ t.dumpIndex(os.Stdout, start, stop)
+}
+
+func (t *freezerTable) dumpIndexString(start, stop int64) string {
+ var out bytes.Buffer
+ out.WriteString("\n")
+ t.dumpIndex(&out, start, stop)
+ return out.String()
+}
+
+func (t *freezerTable) dumpIndex(w io.Writer, start, stop int64) {
buf := make([]byte, indexEntrySize)
- fmt.Printf("| number | fileno | offset |\n")
- fmt.Printf("|--------|--------|--------|\n")
+ fmt.Fprintf(w, "| number | fileno | offset |\n")
+ fmt.Fprintf(w, "|--------|--------|--------|\n")
for i := uint64(start); ; i++ {
if _, err := t.index.ReadAt(buf, int64(i*indexEntrySize)); err != nil {
@@ -786,10 +733,10 @@ func (t *freezerTable) DumpIndex(start, stop int64) {
}
var entry indexEntry
entry.unmarshalBinary(buf)
- fmt.Printf("| %03d | %03d | %03d | \n", i, entry.filenum, entry.offset)
+ fmt.Fprintf(w, "| %03d | %03d | %03d | \n", i, entry.filenum, entry.offset)
if stop > 0 && i >= uint64(stop) {
break
}
}
- fmt.Printf("|--------------------------|\n")
+ fmt.Fprintf(w, "|--------------------------|\n")
}
diff --git a/core/rawdb/freezer_table_test.go b/core/rawdb/freezer_table_test.go
index e8a8b5c463..803809b520 100644
--- a/core/rawdb/freezer_table_test.go
+++ b/core/rawdb/freezer_table_test.go
@@ -18,49 +18,36 @@ package rawdb
import (
"bytes"
- "encoding/binary"
"fmt"
- "io/ioutil"
"math/rand"
"os"
"path/filepath"
- "sync"
"testing"
"time"
"github.com/ethereum/go-ethereum/metrics"
+ "github.com/stretchr/testify/require"
)
func init() {
rand.Seed(time.Now().Unix())
}
-// Gets a chunk of data, filled with 'b'
-func getChunk(size int, b int) []byte {
- data := make([]byte, size)
- for i := range data {
- data[i] = byte(b)
- }
- return data
-}
-
// TestFreezerBasics test initializing a freezertable from scratch, writing to the table,
// and reading it back.
func TestFreezerBasics(t *testing.T) {
t.Parallel()
// set cutoff at 50 bytes
- f, err := newCustomTable(os.TempDir(),
+ f, err := newTable(os.TempDir(),
fmt.Sprintf("unittest-%d", rand.Uint64()),
metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge(), 50, true)
if err != nil {
t.Fatal(err)
}
defer f.Close()
+
// Write 15 bytes 255 times, results in 85 files
- for x := 0; x < 255; x++ {
- data := getChunk(15, x)
- f.Append(uint64(x), data)
- }
+ writeChunks(t, f, 255, 15)
//print(t, f, 0)
//print(t, f, 1)
@@ -98,16 +85,21 @@ func TestFreezerBasicsClosing(t *testing.T) {
f *freezerTable
err error
)
- f, err = newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ f, err = newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
- // Write 15 bytes 255 times, results in 85 files
+
+ // Write 15 bytes 255 times, results in 85 files.
+ // In-between writes, the table is closed and re-opened.
for x := 0; x < 255; x++ {
data := getChunk(15, x)
- f.Append(uint64(x), data)
+ batch := f.newBatch()
+ require.NoError(t, batch.AppendRaw(uint64(x), data))
+ require.NoError(t, batch.commit())
f.Close()
- f, err = newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+
+ f, err = newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
@@ -124,7 +116,7 @@ func TestFreezerBasicsClosing(t *testing.T) {
t.Fatalf("test %d, got \n%x != \n%x", y, got, exp)
}
f.Close()
- f, err = newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ f, err = newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
@@ -137,22 +129,22 @@ func TestFreezerRepairDanglingHead(t *testing.T) {
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
fname := fmt.Sprintf("dangling_headtest-%d", rand.Uint64())
- { // Fill table
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ // Fill table
+ {
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
// Write 15 bytes 255 times
- for x := 0; x < 255; x++ {
- data := getChunk(15, x)
- f.Append(uint64(x), data)
- }
+ writeChunks(t, f, 255, 15)
+
// The last item should be there
if _, err = f.Retrieve(0xfe); err != nil {
t.Fatal(err)
}
f.Close()
}
+
// open the index
idxFile, err := os.OpenFile(filepath.Join(os.TempDir(), fmt.Sprintf("%s.ridx", fname)), os.O_RDWR, 0644)
if err != nil {
@@ -165,9 +157,10 @@ func TestFreezerRepairDanglingHead(t *testing.T) {
}
idxFile.Truncate(stat.Size() - 4)
idxFile.Close()
+
// Now open it again
{
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
@@ -188,22 +181,22 @@ func TestFreezerRepairDanglingHeadLarge(t *testing.T) {
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
fname := fmt.Sprintf("dangling_headtest-%d", rand.Uint64())
- { // Fill a table and close it
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ // Fill a table and close it
+ {
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
// Write 15 bytes 255 times
- for x := 0; x < 0xff; x++ {
- data := getChunk(15, x)
- f.Append(uint64(x), data)
- }
+ writeChunks(t, f, 255, 15)
+
// The last item should be there
if _, err = f.Retrieve(f.items - 1); err != nil {
t.Fatal(err)
}
f.Close()
}
+
// open the index
idxFile, err := os.OpenFile(filepath.Join(os.TempDir(), fmt.Sprintf("%s.ridx", fname)), os.O_RDWR, 0644)
if err != nil {
@@ -213,9 +206,10 @@ func TestFreezerRepairDanglingHeadLarge(t *testing.T) {
// 0-indexEntry, 1-indexEntry, corrupt-indexEntry
idxFile.Truncate(indexEntrySize + indexEntrySize + indexEntrySize/2)
idxFile.Close()
+
// Now open it again
{
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
@@ -228,15 +222,17 @@ func TestFreezerRepairDanglingHeadLarge(t *testing.T) {
t.Errorf("Expected error for missing index entry")
}
// We should now be able to store items again, from item = 1
+ batch := f.newBatch()
for x := 1; x < 0xff; x++ {
- data := getChunk(15, ^x)
- f.Append(uint64(x), data)
+ require.NoError(t, batch.AppendRaw(uint64(x), getChunk(15, ^x)))
}
+ require.NoError(t, batch.commit())
f.Close()
}
+
// And if we open it, we should now be able to read all of them (new values)
{
- f, _ := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ f, _ := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
for y := 1; y < 255; y++ {
exp := getChunk(15, ^y)
got, err := f.Retrieve(uint64(y))
@@ -255,22 +251,21 @@ func TestSnappyDetection(t *testing.T) {
t.Parallel()
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
fname := fmt.Sprintf("snappytest-%d", rand.Uint64())
+
// Open with snappy
{
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
// Write 15 bytes 255 times
- for x := 0; x < 0xff; x++ {
- data := getChunk(15, x)
- f.Append(uint64(x), data)
- }
+ writeChunks(t, f, 255, 15)
f.Close()
}
+
// Open without snappy
{
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, false)
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, false)
if err != nil {
t.Fatal(err)
}
@@ -282,7 +277,7 @@ func TestSnappyDetection(t *testing.T) {
// Open with snappy
{
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
@@ -292,8 +287,8 @@ func TestSnappyDetection(t *testing.T) {
t.Fatalf("expected no error, got %v", err)
}
}
-
}
+
func assertFileSize(f string, size int64) error {
stat, err := os.Stat(f)
if err != nil {
@@ -303,7 +298,6 @@ func assertFileSize(f string, size int64) error {
return fmt.Errorf("error, expected size %d, got %d", size, stat.Size())
}
return nil
-
}
// TestFreezerRepairDanglingIndex checks that if the index has more entries than there are data,
@@ -313,16 +307,15 @@ func TestFreezerRepairDanglingIndex(t *testing.T) {
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
fname := fmt.Sprintf("dangling_indextest-%d", rand.Uint64())
- { // Fill a table and close it
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ // Fill a table and close it
+ {
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
// Write 15 bytes 9 times : 150 bytes
- for x := 0; x < 9; x++ {
- data := getChunk(15, x)
- f.Append(uint64(x), data)
- }
+ writeChunks(t, f, 9, 15)
+
// The last item should be there
if _, err = f.Retrieve(f.items - 1); err != nil {
f.Close()
@@ -331,6 +324,7 @@ func TestFreezerRepairDanglingIndex(t *testing.T) {
f.Close()
// File sizes should be 45, 45, 45 : items[3, 3, 3)
}
+
// Crop third file
fileToCrop := filepath.Join(os.TempDir(), fmt.Sprintf("%s.0002.rdat", fname))
// Truncate third file: 45 ,45, 20
@@ -345,17 +339,18 @@ func TestFreezerRepairDanglingIndex(t *testing.T) {
file.Truncate(20)
file.Close()
}
+
// Open db it again
// It should restore the file(s) to
// 45, 45, 15
// with 3+3+1 items
{
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
+ defer f.Close()
if f.items != 7 {
- f.Close()
t.Fatalf("expected %d items, got %d", 7, f.items)
}
if err := assertFileSize(fileToCrop, 15); err != nil {
@@ -365,30 +360,29 @@ func TestFreezerRepairDanglingIndex(t *testing.T) {
}
func TestFreezerTruncate(t *testing.T) {
-
t.Parallel()
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
fname := fmt.Sprintf("truncation-%d", rand.Uint64())
- { // Fill table
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ // Fill table
+ {
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
// Write 15 bytes 30 times
- for x := 0; x < 30; x++ {
- data := getChunk(15, x)
- f.Append(uint64(x), data)
- }
+ writeChunks(t, f, 30, 15)
+
// The last item should be there
if _, err = f.Retrieve(f.items - 1); err != nil {
t.Fatal(err)
}
f.Close()
}
+
// Reopen, truncate
{
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
@@ -401,9 +395,7 @@ func TestFreezerTruncate(t *testing.T) {
if f.headBytes != 15 {
t.Fatalf("expected %d bytes, got %d", 15, f.headBytes)
}
-
}
-
}
// TestFreezerRepairFirstFile tests a head file with the very first item only half-written.
@@ -412,20 +404,26 @@ func TestFreezerRepairFirstFile(t *testing.T) {
t.Parallel()
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
fname := fmt.Sprintf("truncationfirst-%d", rand.Uint64())
- { // Fill table
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+
+ // Fill table
+ {
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
// Write 80 bytes, splitting out into two files
- f.Append(0, getChunk(40, 0xFF))
- f.Append(1, getChunk(40, 0xEE))
+ batch := f.newBatch()
+ require.NoError(t, batch.AppendRaw(0, getChunk(40, 0xFF)))
+ require.NoError(t, batch.AppendRaw(1, getChunk(40, 0xEE)))
+ require.NoError(t, batch.commit())
+
// The last item should be there
- if _, err = f.Retrieve(f.items - 1); err != nil {
+ if _, err = f.Retrieve(1); err != nil {
t.Fatal(err)
}
f.Close()
}
+
// Truncate the file in half
fileToCrop := filepath.Join(os.TempDir(), fmt.Sprintf("%s.0001.rdat", fname))
{
@@ -439,9 +437,10 @@ func TestFreezerRepairFirstFile(t *testing.T) {
file.Truncate(20)
file.Close()
}
+
// Reopen
{
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
@@ -449,9 +448,14 @@ func TestFreezerRepairFirstFile(t *testing.T) {
f.Close()
t.Fatalf("expected %d items, got %d", 0, f.items)
}
+
// Write 40 bytes
- f.Append(1, getChunk(40, 0xDD))
+ batch := f.newBatch()
+ require.NoError(t, batch.AppendRaw(1, getChunk(40, 0xDD)))
+ require.NoError(t, batch.commit())
+
f.Close()
+
// Should have been truncated down to zero and then 40 written
if err := assertFileSize(fileToCrop, 40); err != nil {
t.Fatal(err)
@@ -468,25 +472,26 @@ func TestFreezerReadAndTruncate(t *testing.T) {
t.Parallel()
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
fname := fmt.Sprintf("read_truncate-%d", rand.Uint64())
- { // Fill table
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+
+ // Fill table
+ {
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
// Write 15 bytes 30 times
- for x := 0; x < 30; x++ {
- data := getChunk(15, x)
- f.Append(uint64(x), data)
- }
+ writeChunks(t, f, 30, 15)
+
// The last item should be there
if _, err = f.Retrieve(f.items - 1); err != nil {
t.Fatal(err)
}
f.Close()
}
+
// Reopen and read all files
{
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
@@ -497,40 +502,48 @@ func TestFreezerReadAndTruncate(t *testing.T) {
for y := byte(0); y < 30; y++ {
f.Retrieve(uint64(y))
}
+
// Now, truncate back to zero
f.truncate(0)
+
// Write the data again
+ batch := f.newBatch()
for x := 0; x < 30; x++ {
- data := getChunk(15, ^x)
- if err := f.Append(uint64(x), data); err != nil {
- t.Fatalf("error %v", err)
- }
+ require.NoError(t, batch.AppendRaw(uint64(x), getChunk(15, ^x)))
}
+ require.NoError(t, batch.commit())
f.Close()
}
}
-func TestOffset(t *testing.T) {
+func TestFreezerOffset(t *testing.T) {
t.Parallel()
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
fname := fmt.Sprintf("offset-%d", rand.Uint64())
- { // Fill table
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 40, true)
+
+ // Fill table
+ {
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true)
if err != nil {
t.Fatal(err)
}
+
// Write 6 x 20 bytes, splitting out into three files
- f.Append(0, getChunk(20, 0xFF))
- f.Append(1, getChunk(20, 0xEE))
+ batch := f.newBatch()
+ require.NoError(t, batch.AppendRaw(0, getChunk(20, 0xFF)))
+ require.NoError(t, batch.AppendRaw(1, getChunk(20, 0xEE)))
- f.Append(2, getChunk(20, 0xdd))
- f.Append(3, getChunk(20, 0xcc))
+ require.NoError(t, batch.AppendRaw(2, getChunk(20, 0xdd)))
+ require.NoError(t, batch.AppendRaw(3, getChunk(20, 0xcc)))
- f.Append(4, getChunk(20, 0xbb))
- f.Append(5, getChunk(20, 0xaa))
- f.DumpIndex(0, 100)
+ require.NoError(t, batch.AppendRaw(4, getChunk(20, 0xbb)))
+ require.NoError(t, batch.AppendRaw(5, getChunk(20, 0xaa)))
+ require.NoError(t, batch.commit())
+
+ t.Log(f.dumpIndexString(0, 100))
f.Close()
}
+
// Now crop it.
{
// delete files 0 and 1
@@ -558,7 +571,7 @@ func TestOffset(t *testing.T) {
filenum: tailId,
offset: itemOffset,
}
- buf := zeroIndex.marshallBinary()
+ buf := zeroIndex.append(nil)
// Overwrite index zero
copy(indexBuf, buf)
// Remove the four next indices by overwriting
@@ -567,44 +580,36 @@ func TestOffset(t *testing.T) {
// Need to truncate the moved index items
indexFile.Truncate(indexEntrySize * (1 + 2))
indexFile.Close()
-
}
+
// Now open again
- checkPresent := func(numDeleted uint64) {
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 40, true)
+ {
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true)
if err != nil {
t.Fatal(err)
}
- f.DumpIndex(0, 100)
- // It should allow writing item 6
- f.Append(numDeleted+2, getChunk(20, 0x99))
+ defer f.Close()
+ t.Log(f.dumpIndexString(0, 100))
- // It should be fine to fetch 4,5,6
- if got, err := f.Retrieve(numDeleted); err != nil {
- t.Fatal(err)
- } else if exp := getChunk(20, 0xbb); !bytes.Equal(got, exp) {
- t.Fatalf("expected %x got %x", exp, got)
- }
- if got, err := f.Retrieve(numDeleted + 1); err != nil {
- t.Fatal(err)
- } else if exp := getChunk(20, 0xaa); !bytes.Equal(got, exp) {
- t.Fatalf("expected %x got %x", exp, got)
- }
- if got, err := f.Retrieve(numDeleted + 2); err != nil {
- t.Fatal(err)
- } else if exp := getChunk(20, 0x99); !bytes.Equal(got, exp) {
- t.Fatalf("expected %x got %x", exp, got)
- }
+ // It should allow writing item 6.
+ batch := f.newBatch()
+ require.NoError(t, batch.AppendRaw(6, getChunk(20, 0x99)))
+ require.NoError(t, batch.commit())
- // It should error at 0, 1,2,3
- for i := numDeleted - 1; i > numDeleted-10; i-- {
- if _, err := f.Retrieve(i); err == nil {
- t.Fatal("expected err")
- }
- }
+ checkRetrieveError(t, f, map[uint64]error{
+ 0: errOutOfBounds,
+ 1: errOutOfBounds,
+ 2: errOutOfBounds,
+ 3: errOutOfBounds,
+ })
+ checkRetrieve(t, f, map[uint64][]byte{
+ 4: getChunk(20, 0xbb),
+ 5: getChunk(20, 0xaa),
+ 6: getChunk(20, 0x99),
+ })
}
- checkPresent(4)
- // Now, let's pretend we have deleted 1M items
+
+ // Edit the index again, with a much larger initial offset of 1M.
{
// Read the index file
p := filepath.Join(os.TempDir(), fmt.Sprintf("%v.ridx", fname))
@@ -624,13 +629,71 @@ func TestOffset(t *testing.T) {
offset: itemOffset,
filenum: tailId,
}
- buf := zeroIndex.marshallBinary()
+ buf := zeroIndex.append(nil)
// Overwrite index zero
copy(indexBuf, buf)
indexFile.WriteAt(indexBuf, 0)
indexFile.Close()
}
- checkPresent(1000000)
+
+ // Check that existing items have been moved to index 1M.
+ {
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true)
+ if err != nil {
+ t.Fatal(err)
+ }
+ defer f.Close()
+ t.Log(f.dumpIndexString(0, 100))
+
+ checkRetrieveError(t, f, map[uint64]error{
+ 0: errOutOfBounds,
+ 1: errOutOfBounds,
+ 2: errOutOfBounds,
+ 3: errOutOfBounds,
+ 999999: errOutOfBounds,
+ })
+ checkRetrieve(t, f, map[uint64][]byte{
+ 1000000: getChunk(20, 0xbb),
+ 1000001: getChunk(20, 0xaa),
+ })
+ }
+}
+
+func checkRetrieve(t *testing.T, f *freezerTable, items map[uint64][]byte) {
+ t.Helper()
+
+ for item, wantBytes := range items {
+ value, err := f.Retrieve(item)
+ if err != nil {
+ t.Fatalf("can't get expected item %d: %v", item, err)
+ }
+ if !bytes.Equal(value, wantBytes) {
+ t.Fatalf("item %d has wrong value %x (want %x)", item, value, wantBytes)
+ }
+ }
+}
+
+func checkRetrieveError(t *testing.T, f *freezerTable, items map[uint64]error) {
+ t.Helper()
+
+ for item, wantError := range items {
+ value, err := f.Retrieve(item)
+ if err == nil {
+ t.Fatalf("unexpected value %x for item %d, want error %v", item, value, wantError)
+ }
+ if err != wantError {
+ t.Fatalf("wrong error for item %d: %v", item, err)
+ }
+ }
+}
+
+// Gets a chunk of data, filled with 'b'
+func getChunk(size int, b int) []byte {
+ data := make([]byte, size)
+ for i := range data {
+ data[i] = byte(b)
+ }
+ return data
}
// TODO (?)
@@ -644,53 +707,18 @@ func TestOffset(t *testing.T) {
// should be handled already, and the case described above can only (?) happen if an
// external process/user deletes files from the filesystem.
-// TestAppendTruncateParallel is a test to check if the Append/truncate operations are
-// racy.
-//
-// The reason why it's not a regular fuzzer, within tests/fuzzers, is that it is dependent
-// on timing rather than 'clever' input -- there's no determinism.
-func TestAppendTruncateParallel(t *testing.T) {
- dir, err := ioutil.TempDir("", "freezer")
- if err != nil {
- t.Fatal(err)
- }
- defer os.RemoveAll(dir)
+func writeChunks(t *testing.T, ft *freezerTable, n int, length int) {
+ t.Helper()
- f, err := newCustomTable(dir, "tmp", metrics.NilMeter{}, metrics.NilMeter{}, metrics.NilGauge{}, 8, true)
- if err != nil {
- t.Fatal(err)
- }
-
- fill := func(mark uint64) []byte {
- data := make([]byte, 8)
- binary.LittleEndian.PutUint64(data, mark)
- return data
- }
-
- for i := 0; i < 5000; i++ {
- f.truncate(0)
- data0 := fill(0)
- f.Append(0, data0)
- data1 := fill(1)
-
- var wg sync.WaitGroup
- wg.Add(2)
- go func() {
- f.truncate(0)
- wg.Done()
- }()
- go func() {
- f.Append(1, data1)
- wg.Done()
- }()
- wg.Wait()
-
- if have, err := f.Retrieve(0); err == nil {
- if !bytes.Equal(have, data0) {
- t.Fatalf("have %x want %x", have, data0)
- }
+ batch := ft.newBatch()
+ for i := 0; i < n; i++ {
+ if err := batch.AppendRaw(uint64(i), getChunk(length, i)); err != nil {
+ t.Fatalf("AppendRaw(%d, ...) returned error: %v", i, err)
}
}
+ if err := batch.commit(); err != nil {
+ t.Fatalf("Commit returned error: %v", err)
+ }
}
// TestSequentialRead does some basic tests on the RetrieveItems.
@@ -698,20 +726,17 @@ func TestSequentialRead(t *testing.T) {
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
fname := fmt.Sprintf("batchread-%d", rand.Uint64())
{ // Fill table
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
// Write 15 bytes 30 times
- for x := 0; x < 30; x++ {
- data := getChunk(15, x)
- f.Append(uint64(x), data)
- }
+ writeChunks(t, f, 30, 15)
f.DumpIndex(0, 30)
f.Close()
}
{ // Open it, iterate, verify iteration
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 50, true)
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 50, true)
if err != nil {
t.Fatal(err)
}
@@ -732,7 +757,7 @@ func TestSequentialRead(t *testing.T) {
}
{ // Open it, iterate, verify byte limit. The byte limit is less than item
// size, so each lookup should only return one item
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 40, true)
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 40, true)
if err != nil {
t.Fatal(err)
}
@@ -761,16 +786,13 @@ func TestSequentialReadByteLimit(t *testing.T) {
rm, wm, sg := metrics.NewMeter(), metrics.NewMeter(), metrics.NewGauge()
fname := fmt.Sprintf("batchread-2-%d", rand.Uint64())
{ // Fill table
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 100, true)
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 100, true)
if err != nil {
t.Fatal(err)
}
// Write 10 bytes 30 times,
// Splitting it at every 100 bytes (10 items)
- for x := 0; x < 30; x++ {
- data := getChunk(10, x)
- f.Append(uint64(x), data)
- }
+ writeChunks(t, f, 30, 10)
f.Close()
}
for i, tc := range []struct {
@@ -786,7 +808,7 @@ func TestSequentialReadByteLimit(t *testing.T) {
{100, 109, 10},
} {
{
- f, err := newCustomTable(os.TempDir(), fname, rm, wm, sg, 100, true)
+ f, err := newTable(os.TempDir(), fname, rm, wm, sg, 100, true)
if err != nil {
t.Fatal(err)
}
diff --git a/core/rawdb/freezer_test.go b/core/rawdb/freezer_test.go
new file mode 100644
index 0000000000..7359131c89
--- /dev/null
+++ b/core/rawdb/freezer_test.go
@@ -0,0 +1,301 @@
+// 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 rawdb
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "io/ioutil"
+ "math/big"
+ "math/rand"
+ "os"
+ "sync"
+ "testing"
+
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/rlp"
+ "github.com/stretchr/testify/require"
+)
+
+var freezerTestTableDef = map[string]bool{"test": true}
+
+func TestFreezerModify(t *testing.T) {
+ t.Parallel()
+
+ // Create test data.
+ var valuesRaw [][]byte
+ var valuesRLP []*big.Int
+ for x := 0; x < 100; x++ {
+ v := getChunk(256, x)
+ valuesRaw = append(valuesRaw, v)
+ iv := big.NewInt(int64(x))
+ iv = iv.Exp(iv, iv, nil)
+ valuesRLP = append(valuesRLP, iv)
+ }
+
+ tables := map[string]bool{"raw": true, "rlp": false}
+ f, dir := newFreezerForTesting(t, tables)
+ defer os.RemoveAll(dir)
+ defer f.Close()
+
+ // Commit test data.
+ _, err := f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
+ for i := range valuesRaw {
+ if err := op.AppendRaw("raw", uint64(i), valuesRaw[i]); err != nil {
+ return err
+ }
+ if err := op.Append("rlp", uint64(i), valuesRLP[i]); err != nil {
+ return err
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ t.Fatal("ModifyAncients failed:", err)
+ }
+
+ // Dump indexes.
+ for _, table := range f.tables {
+ t.Log(table.name, "index:", table.dumpIndexString(0, int64(len(valuesRaw))))
+ }
+
+ // Read back test data.
+ checkAncientCount(t, f, "raw", uint64(len(valuesRaw)))
+ checkAncientCount(t, f, "rlp", uint64(len(valuesRLP)))
+ for i := range valuesRaw {
+ v, _ := f.Ancient("raw", uint64(i))
+ if !bytes.Equal(v, valuesRaw[i]) {
+ t.Fatalf("wrong raw value at %d: %x", i, v)
+ }
+ ivEnc, _ := f.Ancient("rlp", uint64(i))
+ want, _ := rlp.EncodeToBytes(valuesRLP[i])
+ if !bytes.Equal(ivEnc, want) {
+ t.Fatalf("wrong RLP value at %d: %x", i, ivEnc)
+ }
+ }
+}
+
+// This checks that ModifyAncients rolls back freezer updates
+// when the function passed to it returns an error.
+func TestFreezerModifyRollback(t *testing.T) {
+ t.Parallel()
+
+ f, dir := newFreezerForTesting(t, freezerTestTableDef)
+ defer os.RemoveAll(dir)
+
+ theError := errors.New("oops")
+ _, err := f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
+ // Append three items. This creates two files immediately,
+ // because the table size limit of the test freezer is 2048.
+ require.NoError(t, op.AppendRaw("test", 0, make([]byte, 2048)))
+ require.NoError(t, op.AppendRaw("test", 1, make([]byte, 2048)))
+ require.NoError(t, op.AppendRaw("test", 2, make([]byte, 2048)))
+ return theError
+ })
+ if err != theError {
+ t.Errorf("ModifyAncients returned wrong error %q", err)
+ }
+ checkAncientCount(t, f, "test", 0)
+ f.Close()
+
+ // Reopen and check that the rolled-back data doesn't reappear.
+ tables := map[string]bool{"test": true}
+ f2, err := newFreezer(dir, "", false, 2049, tables)
+ if err != nil {
+ t.Fatalf("can't reopen freezer after failed ModifyAncients: %v", err)
+ }
+ defer f2.Close()
+ checkAncientCount(t, f2, "test", 0)
+}
+
+// This test runs ModifyAncients and Ancient concurrently with each other.
+func TestFreezerConcurrentModifyRetrieve(t *testing.T) {
+ t.Parallel()
+
+ f, dir := newFreezerForTesting(t, freezerTestTableDef)
+ defer os.RemoveAll(dir)
+ defer f.Close()
+
+ var (
+ numReaders = 5
+ writeBatchSize = uint64(50)
+ written = make(chan uint64, numReaders*6)
+ wg sync.WaitGroup
+ )
+ wg.Add(numReaders + 1)
+
+ // Launch the writer. It appends 10000 items in batches.
+ go func() {
+ defer wg.Done()
+ defer close(written)
+ for item := uint64(0); item < 10000; item += writeBatchSize {
+ _, err := f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
+ for i := uint64(0); i < writeBatchSize; i++ {
+ item := item + i
+ value := getChunk(32, int(item))
+ if err := op.AppendRaw("test", item, value); err != nil {
+ return err
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ panic(err)
+ }
+ for i := 0; i < numReaders; i++ {
+ written <- item + writeBatchSize
+ }
+ }
+ }()
+
+ // Launch the readers. They read random items from the freezer up to the
+ // current frozen item count.
+ for i := 0; i < numReaders; i++ {
+ go func() {
+ defer wg.Done()
+ for frozen := range written {
+ for rc := 0; rc < 80; rc++ {
+ num := uint64(rand.Intn(int(frozen)))
+ value, err := f.Ancient("test", num)
+ if err != nil {
+ panic(fmt.Errorf("error reading %d (frozen %d): %v", num, frozen, err))
+ }
+ if !bytes.Equal(value, getChunk(32, int(num))) {
+ panic(fmt.Errorf("wrong value at %d", num))
+ }
+ }
+ }
+ }()
+ }
+
+ wg.Wait()
+}
+
+// This test runs ModifyAncients and TruncateAncients concurrently with each other.
+func TestFreezerConcurrentModifyTruncate(t *testing.T) {
+ f, dir := newFreezerForTesting(t, freezerTestTableDef)
+ defer os.RemoveAll(dir)
+ defer f.Close()
+
+ var item = make([]byte, 256)
+
+ for i := 0; i < 1000; i++ {
+ // First reset and write 100 items.
+ if err := f.TruncateAncients(0); err != nil {
+ t.Fatal("truncate failed:", err)
+ }
+ _, err := f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
+ for i := uint64(0); i < 100; i++ {
+ if err := op.AppendRaw("test", i, item); err != nil {
+ return err
+ }
+ }
+ return nil
+ })
+ if err != nil {
+ t.Fatal("modify failed:", err)
+ }
+ checkAncientCount(t, f, "test", 100)
+
+ // Now append 100 more items and truncate concurrently.
+ var (
+ wg sync.WaitGroup
+ truncateErr error
+ modifyErr error
+ )
+ wg.Add(3)
+ go func() {
+ _, modifyErr = f.ModifyAncients(func(op ethdb.AncientWriteOp) error {
+ for i := uint64(100); i < 200; i++ {
+ if err := op.AppendRaw("test", i, item); err != nil {
+ return err
+ }
+ }
+ return nil
+ })
+ wg.Done()
+ }()
+ go func() {
+ truncateErr = f.TruncateAncients(10)
+ wg.Done()
+ }()
+ go func() {
+ f.AncientSize("test")
+ wg.Done()
+ }()
+ wg.Wait()
+
+ // Now check the outcome. If the truncate operation went through first, the append
+ // fails, otherwise it succeeds. In either case, the freezer should be positioned
+ // at 10 after both operations are done.
+ if truncateErr != nil {
+ t.Fatal("concurrent truncate failed:", err)
+ }
+ if !(modifyErr == nil || modifyErr == errOutOrderInsertion) {
+ t.Fatal("wrong error from concurrent modify:", modifyErr)
+ }
+ checkAncientCount(t, f, "test", 10)
+ }
+}
+
+func newFreezerForTesting(t *testing.T, tables map[string]bool) (*freezer, string) {
+ t.Helper()
+
+ dir, err := ioutil.TempDir("", "freezer")
+ if err != nil {
+ t.Fatal(err)
+ }
+ // note: using low max table size here to ensure the tests actually
+ // switch between multiple files.
+ f, err := newFreezer(dir, "", false, 2049, tables)
+ if err != nil {
+ t.Fatal("can't open freezer", err)
+ }
+ return f, dir
+}
+
+// checkAncientCount verifies that the freezer contains n items.
+func checkAncientCount(t *testing.T, f *freezer, kind string, n uint64) {
+ t.Helper()
+
+ if frozen, _ := f.Ancients(); frozen != n {
+ t.Fatalf("Ancients() returned %d, want %d", frozen, n)
+ }
+
+ // Check at index n-1.
+ if n > 0 {
+ index := n - 1
+ if ok, _ := f.HasAncient(kind, index); !ok {
+ t.Errorf("HasAncient(%q, %d) returned false unexpectedly", kind, index)
+ }
+ if _, err := f.Ancient(kind, index); err != nil {
+ t.Errorf("Ancient(%q, %d) returned unexpected error %q", kind, index, err)
+ }
+ }
+
+ // Check at index n.
+ index := n
+ if ok, _ := f.HasAncient(kind, index); ok {
+ t.Errorf("HasAncient(%q, %d) returned true unexpectedly", kind, index)
+ }
+ if _, err := f.Ancient(kind, index); err == nil {
+ t.Errorf("Ancient(%q, %d) didn't return expected error", kind, index)
+ } else if err != errOutOfBounds {
+ t.Errorf("Ancient(%q, %d) returned unexpected error %q", kind, index, err)
+ }
+}
diff --git a/core/rawdb/table.go b/core/rawdb/table.go
index 586451c064..91fc31b660 100644
--- a/core/rawdb/table.go
+++ b/core/rawdb/table.go
@@ -62,10 +62,10 @@ func (t *table) Ancient(kind string, number uint64) ([]byte, error) {
return t.db.Ancient(kind, number)
}
-// ReadAncients is a noop passthrough that just forwards the request to the underlying
+// AncientRange is a noop passthrough that just forwards the request to the underlying
// database.
-func (t *table) ReadAncients(kind string, start, count, maxBytes uint64) ([][]byte, error) {
- return t.db.ReadAncients(kind, start, count, maxBytes)
+func (t *table) AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error) {
+ return t.db.AncientRange(kind, start, count, maxBytes)
}
// Ancients is a noop passthrough that just forwards the request to the underlying
@@ -80,10 +80,13 @@ func (t *table) AncientSize(kind string) (uint64, error) {
return t.db.AncientSize(kind)
}
-// AppendAncient is a noop passthrough that just forwards the request to the underlying
-// database.
-func (t *table) AppendAncient(number uint64, hash, header, body, receipts, td []byte) error {
- return t.db.AppendAncient(number, hash, header, body, receipts, td)
+// ModifyAncients runs an ancient write operation on the underlying database.
+func (t *table) ModifyAncients(fn func(ethdb.AncientWriteOp) error) (int64, error) {
+ return t.db.ModifyAncients(fn)
+}
+
+func (t *table) ReadAncients(fn func(reader ethdb.AncientReader) error) (err error) {
+ return t.db.ReadAncients(fn)
}
// TruncateAncients is a noop passthrough that just forwards the request to the underlying
diff --git a/core/rawdb/testdata/stored_receipts.bin b/core/rawdb/testdata/stored_receipts.bin
new file mode 100644
index 0000000000..8204fae09b
Binary files /dev/null and b/core/rawdb/testdata/stored_receipts.bin differ
diff --git a/core/rlp_test.go b/core/rlp_test.go
index 3a90811e74..40bcef5e55 100644
--- a/core/rlp_test.go
+++ b/core/rlp_test.go
@@ -40,7 +40,7 @@ func getBlock(transactions int, uncles int, dataSize int) *types.Block {
// A sender who makes transactions, has some funds
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
address = crypto.PubkeyToAddress(key.PublicKey)
- funds = big.NewInt(1000000000000000)
+ funds = big.NewInt(1_000_000_000_000_000_000)
gspec = &Genesis{
Config: params.TestChainConfig,
Alloc: GenesisAlloc{address: {Balance: funds}},
diff --git a/core/state/database.go b/core/state/database.go
index 1a06e33409..bbcd2358e5 100644
--- a/core/state/database.go
+++ b/core/state/database.go
@@ -23,6 +23,7 @@ import (
"github.com/VictoriaMetrics/fastcache"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/trie"
lru "github.com/hashicorp/golang-lru"
@@ -70,6 +71,9 @@ type Trie interface {
// trie.MissingNodeError is returned.
TryGet(key []byte) ([]byte, error)
+ // TryUpdateAccount abstract an account write in the trie.
+ TryUpdateAccount(key []byte, account *types.StateAccount) error
+
// TryUpdate associates key with value in the trie. If value has length zero, any
// existing value is deleted from the trie. The value bytes must not be modified
// by the caller while they are stored in the trie. If a node was not found in the
@@ -86,7 +90,7 @@ type Trie interface {
// Commit writes all nodes to the trie's memory database, tracking the internal
// and external (for account tries) references.
- Commit(onleaf trie.LeafCallback) (common.Hash, error)
+ Commit(onleaf trie.LeafCallback) (common.Hash, int, error)
// NodeIterator returns an iterator that returns nodes of the trie. Iteration
// starts at the key after the given start key.
diff --git a/core/state/dump.go b/core/state/dump.go
index 00faa4ed6a..bfcc035435 100644
--- a/core/state/dump.go
+++ b/core/state/dump.go
@@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
@@ -140,7 +141,7 @@ func (s *StateDB) DumpToCollector(c DumpCollector, conf *DumpConfig) (nextKey []
it := trie.NewIterator(s.trie.NodeIterator(conf.Start))
for it.Next() {
- var data Account
+ var data types.StateAccount
if err := rlp.DecodeBytes(it.Value, &data); err != nil {
panic(err)
}
diff --git a/core/state/iterator.go b/core/state/iterator.go
index 6a5c73d3d1..611df52431 100644
--- a/core/state/iterator.go
+++ b/core/state/iterator.go
@@ -21,6 +21,7 @@ import (
"fmt"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
@@ -104,7 +105,7 @@ func (it *NodeIterator) step() error {
return nil
}
// Otherwise we've reached an account node, initiate data iteration
- var account Account
+ var account types.StateAccount
if err := rlp.Decode(bytes.NewReader(it.stateIt.LeafBlob()), &account); err != nil {
return err
}
diff --git a/core/state/journal.go b/core/state/journal.go
index 2070f30875..57a692dc7f 100644
--- a/core/state/journal.go
+++ b/core/state/journal.go
@@ -33,14 +33,14 @@ type journalEntry interface {
}
// journal contains the list of state modifications applied since the last state
-// commit. These are tracked to be able to be reverted in case of an execution
-// exception or revertal request.
+// commit. These are tracked to be able to be reverted in the case of an execution
+// exception or request for reversal.
type journal struct {
entries []journalEntry // Current changes tracked by the journal
dirties map[common.Address]int // Dirty accounts and the number of changes
}
-// newJournal create a new initialized journal.
+// newJournal creates a new initialized journal.
func newJournal() *journal {
return &journal{
dirties: make(map[common.Address]int),
diff --git a/core/state/metrics.go b/core/state/metrics.go
new file mode 100644
index 0000000000..7b40ff37af
--- /dev/null
+++ b/core/state/metrics.go
@@ -0,0 +1,28 @@
+// 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 state
+
+import "github.com/ethereum/go-ethereum/metrics"
+
+var (
+ accountUpdatedMeter = metrics.NewRegisteredMeter("state/update/account", nil)
+ storageUpdatedMeter = metrics.NewRegisteredMeter("state/update/storage", nil)
+ accountDeletedMeter = metrics.NewRegisteredMeter("state/delete/account", nil)
+ storageDeletedMeter = metrics.NewRegisteredMeter("state/delete/storage", nil)
+ accountCommittedMeter = metrics.NewRegisteredMeter("state/commit/account", nil)
+ storageCommittedMeter = metrics.NewRegisteredMeter("state/commit/storage", nil)
+)
diff --git a/core/state/pruner/pruner.go b/core/state/pruner/pruner.go
index 4d6e415511..37772ca35c 100644
--- a/core/state/pruner/pruner.go
+++ b/core/state/pruner/pruner.go
@@ -29,7 +29,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/snapshot"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
@@ -426,7 +425,7 @@ func extractGenesis(db ethdb.Database, stateBloom *stateBloom) error {
// If it's a leaf node, yes we are touching an account,
// dig into the storage trie further.
if accIter.Leaf() {
- var acc state.Account
+ var acc types.StateAccount
if err := rlp.DecodeBytes(accIter.LeafBlob(), &acc); err != nil {
return err
}
diff --git a/core/state/snapshot/difflayer_test.go b/core/state/snapshot/difflayer_test.go
index 919af5fa86..e15c1d5049 100644
--- a/core/state/snapshot/difflayer_test.go
+++ b/core/state/snapshot/difflayer_test.go
@@ -388,7 +388,7 @@ func BenchmarkJournal(b *testing.B) {
}
return newDiffLayer(parent, common.Hash{}, destructs, accounts, storage)
}
- layer := snapshot(new(diskLayer))
+ layer := snapshot(emptyLayer())
for i := 1; i < 128; i++ {
layer = fill(layer)
}
diff --git a/core/state/snapshot/generate.go b/core/state/snapshot/generate.go
index 7e29e51b21..a4373d8bcd 100644
--- a/core/state/snapshot/generate.go
+++ b/core/state/snapshot/generate.go
@@ -436,7 +436,7 @@ func (dl *diskLayer) generateRange(root common.Hash, prefix []byte, kind string,
for i, key := range result.keys {
snapTrie.Update(key, result.vals[i])
}
- root, _ := snapTrie.Commit(nil)
+ root, _, _ := snapTrie.Commit(nil)
snapTrieDb.Commit(root, false, nil)
}
tr := result.tr
@@ -560,6 +560,12 @@ func (dl *diskLayer) generate(stats *generatorStats) {
default:
}
if batch.ValueSize() > ethdb.IdealBatchSize || abort != nil {
+ if bytes.Compare(currentLocation, dl.genMarker) < 0 {
+ log.Error("Snapshot generator went backwards",
+ "currentLocation", fmt.Sprintf("%x", currentLocation),
+ "genMarker", fmt.Sprintf("%x", dl.genMarker))
+ }
+
// Flush out the batch anyway no matter it's empty or not.
// It's possible that all the states are recovered and the
// generation indeed makes progress.
@@ -634,8 +640,14 @@ func (dl *diskLayer) generate(stats *generatorStats) {
stats.storage += common.StorageSize(1 + common.HashLength + dataLen)
stats.accounts++
}
+ marker := accountHash[:]
+ // If the snap generation goes here after interrupted, genMarker may go backward
+ // when last genMarker is consisted of accountHash and storageHash
+ if accMarker != nil && bytes.Equal(marker, accMarker) && len(dl.genMarker) > common.HashLength {
+ marker = dl.genMarker[:]
+ }
// If we've exceeded our batch allowance or termination was requested, flush to disk
- if err := checkAndFlush(accountHash[:]); err != nil {
+ if err := checkAndFlush(marker); err != nil {
return err
}
// If the iterated account is the contract, create a further loop to
diff --git a/core/state/snapshot/generate_test.go b/core/state/snapshot/generate_test.go
index a92517b315..582da6a2e7 100644
--- a/core/state/snapshot/generate_test.go
+++ b/core/state/snapshot/generate_test.go
@@ -60,7 +60,7 @@ func TestGeneration(t *testing.T) {
acc = &Account{Balance: big.NewInt(3), Root: stTrie.Hash().Bytes(), CodeHash: emptyCode.Bytes()}
val, _ = rlp.EncodeToBytes(acc)
accTrie.Update([]byte("acc-3"), val) // 0x50815097425d000edfc8b3a4a13e175fc2bdcfee8bdfbf2d1ff61041d3c235b2
- root, _ := accTrie.Commit(nil) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd
+ root, _, _ := accTrie.Commit(nil) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd
triedb.Commit(root, false, nil)
if have, want := root, common.HexToHash("0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd"); have != want {
@@ -128,7 +128,7 @@ func TestGenerateExistentState(t *testing.T) {
rawdb.WriteStorageSnapshot(diskdb, hashData([]byte("acc-3")), hashData([]byte("key-2")), []byte("val-2"))
rawdb.WriteStorageSnapshot(diskdb, hashData([]byte("acc-3")), hashData([]byte("key-3")), []byte("val-3"))
- root, _ := accTrie.Commit(nil) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd
+ root, _, _ := accTrie.Commit(nil) // Root: 0xe3712f1a226f3782caca78ca770ccc19ee000552813a9f59d479f8611db9b1fd
triedb.Commit(root, false, nil)
snap := generateSnapshot(diskdb, triedb, 16, root)
@@ -215,12 +215,12 @@ func (t *testHelper) makeStorageTrie(keys []string, vals []string) []byte {
for i, k := range keys {
stTrie.Update([]byte(k), []byte(vals[i]))
}
- root, _ := stTrie.Commit(nil)
+ root, _, _ := stTrie.Commit(nil)
return root.Bytes()
}
func (t *testHelper) Generate() (common.Hash, *diskLayer) {
- root, _ := t.accTrie.Commit(nil)
+ root, _, _ := t.accTrie.Commit(nil)
t.triedb.Commit(root, false, nil)
snap := generateSnapshot(t.diskdb, t.triedb, 16, root)
return root, snap
@@ -575,7 +575,7 @@ func TestGenerateWithExtraAccounts(t *testing.T) {
rawdb.WriteStorageSnapshot(diskdb, key, hashData([]byte("b-key-2")), []byte("b-val-2"))
rawdb.WriteStorageSnapshot(diskdb, key, hashData([]byte("b-key-3")), []byte("b-val-3"))
}
- root, _ := accTrie.Commit(nil)
+ root, _, _ := accTrie.Commit(nil)
t.Logf("root: %x", root)
triedb.Commit(root, false, nil)
// To verify the test: If we now inspect the snap db, there should exist extraneous storage items
@@ -637,7 +637,7 @@ func TestGenerateWithManyExtraAccounts(t *testing.T) {
rawdb.WriteAccountSnapshot(diskdb, key, val)
}
}
- root, _ := accTrie.Commit(nil)
+ root, _, _ := accTrie.Commit(nil)
t.Logf("root: %x", root)
triedb.Commit(root, false, nil)
@@ -690,7 +690,7 @@ func TestGenerateWithExtraBeforeAndAfter(t *testing.T) {
rawdb.WriteAccountSnapshot(diskdb, common.HexToHash("0x07"), val)
}
- root, _ := accTrie.Commit(nil)
+ root, _, _ := accTrie.Commit(nil)
t.Logf("root: %x", root)
triedb.Commit(root, false, nil)
@@ -734,7 +734,7 @@ func TestGenerateWithMalformedSnapdata(t *testing.T) {
rawdb.WriteAccountSnapshot(diskdb, common.HexToHash("0x05"), junk)
}
- root, _ := accTrie.Commit(nil)
+ root, _, _ := accTrie.Commit(nil)
t.Logf("root: %x", root)
triedb.Commit(root, false, nil)
diff --git a/core/state/snapshot/iterator.go b/core/state/snapshot/iterator.go
index 1d9340bbad..c1a196c7ff 100644
--- a/core/state/snapshot/iterator.go
+++ b/core/state/snapshot/iterator.go
@@ -385,7 +385,7 @@ func (it *diskStorageIterator) Hash() common.Hash {
return common.BytesToHash(it.it.Key()) // The prefix will be truncated
}
-// Slot returns the raw strorage slot content the iterator is currently at.
+// Slot returns the raw storage slot content the iterator is currently at.
func (it *diskStorageIterator) Slot() []byte {
return it.it.Value()
}
diff --git a/core/state/snapshot/snapshot.go b/core/state/snapshot/snapshot.go
index 8889fa9fec..6ee6b06bb5 100644
--- a/core/state/snapshot/snapshot.go
+++ b/core/state/snapshot/snapshot.go
@@ -163,6 +163,9 @@ type Tree struct {
cache int // Megabytes permitted to use for read caches
layers map[common.Hash]snapshot // Collection of all known layers
lock sync.RWMutex
+
+ // Test hooks
+ onFlatten func() // Hook invoked when the bottom most diff layers are flattened
}
// New attempts to load an already existing snapshot from a persistent key-value
@@ -463,14 +466,21 @@ func (t *Tree) cap(diff *diffLayer, layers int) *diskLayer {
return nil
case *diffLayer:
+ // Hold the write lock until the flattened parent is linked correctly.
+ // Otherwise, the stale layer may be accessed by external reads in the
+ // meantime.
+ diff.lock.Lock()
+ defer diff.lock.Unlock()
+
// Flatten the parent into the grandparent. The flattening internally obtains a
// write lock on grandparent.
flattened := parent.flatten().(*diffLayer)
t.layers[flattened.root] = flattened
- diff.lock.Lock()
- defer diff.lock.Unlock()
-
+ // Invoke the hook if it's registered. Ugly hack.
+ if t.onFlatten != nil {
+ t.onFlatten()
+ }
diff.parent = flattened
if flattened.memory < aggregatorMemoryLimit {
// Accumulator layer is smaller than the limit, so we can abort, unless
diff --git a/core/state/snapshot/snapshot_test.go b/core/state/snapshot/snapshot_test.go
index 4b787cfe2e..12f2765b3b 100644
--- a/core/state/snapshot/snapshot_test.go
+++ b/core/state/snapshot/snapshot_test.go
@@ -22,6 +22,7 @@ import (
"math/big"
"math/rand"
"testing"
+ "time"
"github.com/VictoriaMetrics/fastcache"
"github.com/ethereum/go-ethereum/common"
@@ -324,7 +325,7 @@ func TestPostCapBasicDataAccess(t *testing.T) {
}
}
-// TestSnaphots tests the functionality for retrieveing the snapshot
+// TestSnaphots tests the functionality for retrieving the snapshot
// with given head root and the desired depth.
func TestSnaphots(t *testing.T) {
// setAccount is a helper to construct a random account entry and assign it to
@@ -423,3 +424,63 @@ func TestSnaphots(t *testing.T) {
}
}
}
+
+// TestReadStateDuringFlattening tests the scenario that, during the
+// bottom diff layers are merging which tags these as stale, the read
+// happens via a pre-created top snapshot layer which tries to access
+// the state in these stale layers. Ensure this read can retrieve the
+// right state back(block until the flattening is finished) instead of
+// an unexpected error(snapshot layer is stale).
+func TestReadStateDuringFlattening(t *testing.T) {
+ // setAccount is a helper to construct a random account entry and assign it to
+ // an account slot in a snapshot
+ setAccount := func(accKey string) map[common.Hash][]byte {
+ return map[common.Hash][]byte{
+ common.HexToHash(accKey): randomAccount(),
+ }
+ }
+ // Create a starting base layer and a snapshot tree out of it
+ base := &diskLayer{
+ diskdb: rawdb.NewMemoryDatabase(),
+ root: common.HexToHash("0x01"),
+ cache: fastcache.New(1024 * 500),
+ }
+ snaps := &Tree{
+ layers: map[common.Hash]snapshot{
+ base.root: base,
+ },
+ }
+ // 4 layers in total, 3 diff layers and 1 disk layers
+ snaps.Update(common.HexToHash("0xa1"), common.HexToHash("0x01"), nil, setAccount("0xa1"), nil)
+ snaps.Update(common.HexToHash("0xa2"), common.HexToHash("0xa1"), nil, setAccount("0xa2"), nil)
+ snaps.Update(common.HexToHash("0xa3"), common.HexToHash("0xa2"), nil, setAccount("0xa3"), nil)
+
+ // Obtain the topmost snapshot handler for state accessing
+ snap := snaps.Snapshot(common.HexToHash("0xa3"))
+
+ // Register the testing hook to access the state after flattening
+ var result = make(chan *Account)
+ snaps.onFlatten = func() {
+ // Spin up a thread to read the account from the pre-created
+ // snapshot handler. It's expected to be blocked.
+ go func() {
+ account, _ := snap.Account(common.HexToHash("0xa1"))
+ result <- account
+ }()
+ select {
+ case res := <-result:
+ t.Fatalf("Unexpected return %v", res)
+ case <-time.NewTimer(time.Millisecond * 300).C:
+ }
+ }
+ // Cap the snap tree, which will mark the bottom-most layer as stale.
+ snaps.Cap(common.HexToHash("0xa3"), 1)
+ select {
+ case account := <-result:
+ if account == nil {
+ t.Fatal("Failed to retrieve account")
+ }
+ case <-time.NewTimer(time.Millisecond * 300).C:
+ t.Fatal("Unexpected blocker")
+ }
+}
diff --git a/core/state/state_object.go b/core/state/state_object.go
index 38621ffb61..138fcbdecd 100644
--- a/core/state/state_object.go
+++ b/core/state/state_object.go
@@ -24,6 +24,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/metrics"
"github.com/ethereum/go-ethereum/rlp"
@@ -65,7 +66,7 @@ func (s Storage) Copy() Storage {
type stateObject struct {
address common.Address
addrHash common.Hash // hash of ethereum address of the account
- data Account
+ data types.StateAccount
db *StateDB
// DB error.
@@ -97,17 +98,8 @@ func (s *stateObject) empty() bool {
return s.data.Nonce == 0 && s.data.Balance.Sign() == 0 && bytes.Equal(s.data.CodeHash, emptyCodeHash)
}
-// Account is the Ethereum consensus representation of accounts.
-// These objects are stored in the main account trie.
-type Account struct {
- Nonce uint64
- Balance *big.Int
- Root common.Hash // merkle root of the storage trie
- CodeHash []byte
-}
-
// newObject creates a state object.
-func newObject(db *StateDB, address common.Address, data Account) *stateObject {
+func newObject(db *StateDB, address common.Address, data types.StateAccount) *stateObject {
if data.Balance == nil {
data.Balance = new(big.Int)
}
@@ -130,7 +122,7 @@ func newObject(db *StateDB, address common.Address, data Account) *stateObject {
// EncodeRLP implements rlp.Encoder.
func (s *stateObject) EncodeRLP(w io.Writer) error {
- return rlp.Encode(w, s.data)
+ return rlp.Encode(w, &s.data)
}
// setError remembers the first non-nil error it is called with.
@@ -236,7 +228,7 @@ func (s *stateObject) GetCommittedState(db Database, key common.Hash) common.Has
}
enc, err = s.db.snap.Storage(s.addrHash, crypto.Keccak256Hash(key.Bytes()))
}
- // If snapshot unavailable or reading from it failed, load from the database
+ // If the snapshot is unavailable or reading from it fails, load from the database.
if s.db.snap == nil || err != nil {
if meter != nil {
// If we already spent time checking the snapshot, account for it
@@ -329,7 +321,7 @@ func (s *stateObject) finalise(prefetch bool) {
// It will return nil if the trie has not been loaded and no changes have been made
func (s *stateObject) updateTrie(db Database) Trie {
// Make sure all dirty slots are finalized into the pending storage area
- s.finalise(false) // Don't prefetch any more, pull directly if need be
+ s.finalise(false) // Don't prefetch anymore, pull directly if need be
if len(s.pendingStorage) == 0 {
return s.trie
}
@@ -354,10 +346,12 @@ func (s *stateObject) updateTrie(db Database) Trie {
var v []byte
if (value == common.Hash{}) {
s.setError(tr.TryDelete(key[:]))
+ s.db.StorageDeleted += 1
} else {
// Encoding []byte cannot fail, ok to ignore the error.
v, _ = rlp.EncodeToBytes(common.TrimLeftZeroes(value[:]))
s.setError(tr.TryUpdate(key[:], v))
+ s.db.StorageUpdated += 1
}
// If state snapshotting is active, cache the data til commit
if s.db.snap != nil {
@@ -368,7 +362,7 @@ func (s *stateObject) updateTrie(db Database) Trie {
s.db.snapStorage[s.addrHash] = storage
}
}
- storage[crypto.HashData(hasher, key[:])] = v // v will be nil if value is 0x00
+ storage[crypto.HashData(hasher, key[:])] = v // v will be nil if it's deleted
}
usedStorage = append(usedStorage, common.CopyBytes(key[:])) // Copy needed for closure
}
@@ -396,23 +390,23 @@ func (s *stateObject) updateRoot(db Database) {
// CommitTrie the storage trie of the object to db.
// This updates the trie root.
-func (s *stateObject) CommitTrie(db Database) error {
+func (s *stateObject) CommitTrie(db Database) (int, error) {
// If nothing changed, don't bother with hashing anything
if s.updateTrie(db) == nil {
- return nil
+ return 0, nil
}
if s.dbErr != nil {
- return s.dbErr
+ return 0, s.dbErr
}
// Track the amount of time wasted on committing the storage trie
if metrics.EnabledExpensive {
defer func(start time.Time) { s.db.StorageCommits += time.Since(start) }(time.Now())
}
- root, err := s.trie.Commit(nil)
+ root, committed, err := s.trie.Commit(nil)
if err == nil {
s.data.Root = root
}
- return err
+ return committed, err
}
// AddBalance adds amount to s's balance.
diff --git a/core/state/statedb.go b/core/state/statedb.go
index 61f0f5b9f6..e3541339ea 100644
--- a/core/state/statedb.go
+++ b/core/state/statedb.go
@@ -117,6 +117,11 @@ type StateDB struct {
SnapshotAccountReads time.Duration
SnapshotStorageReads time.Duration
SnapshotCommits time.Duration
+
+ AccountUpdated int
+ StorageUpdated int
+ AccountDeleted int
+ StorageDeleted int
}
// New creates a new state from a given trie.
@@ -455,12 +460,7 @@ func (s *StateDB) updateStateObject(obj *stateObject) {
}
// Encode the account and update the account trie
addr := obj.Address()
-
- data, err := rlp.EncodeToBytes(obj)
- if err != nil {
- panic(fmt.Errorf("can't encode object at %x: %v", addr[:], err))
- }
- if err = s.trie.TryUpdate(addr[:], data); err != nil {
+ if err := s.trie.TryUpdateAccount(addr[:], &obj.data); err != nil {
s.setError(fmt.Errorf("updateStateObject (%x) error: %v", addr[:], err))
}
@@ -507,7 +507,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
}
// If no live objects are available, attempt to use snapshots
var (
- data *Account
+ data *types.StateAccount
err error
)
if s.snap != nil {
@@ -519,7 +519,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
if acc == nil {
return nil
}
- data = &Account{
+ data = &types.StateAccount{
Nonce: acc.Nonce,
Balance: acc.Balance,
CodeHash: acc.CodeHash,
@@ -546,7 +546,7 @@ func (s *StateDB) getDeletedStateObject(addr common.Address) *stateObject {
if len(enc) == 0 {
return nil
}
- data = new(Account)
+ data = new(types.StateAccount)
if err := rlp.DecodeBytes(enc, data); err != nil {
log.Error("Failed to decode state object", "addr", addr, "err", err)
return nil
@@ -583,7 +583,7 @@ func (s *StateDB) createObject(addr common.Address) (newobj, prev *stateObject)
s.snapDestructs[prev.addrHash] = struct{}{}
}
}
- newobj = newObject(s, addr, Account{})
+ newobj = newObject(s, addr, types.StateAccount{})
if prev == nil {
s.journal.append(createObjectChange{account: &addr})
} else {
@@ -860,8 +860,10 @@ func (s *StateDB) IntermediateRoot(deleteEmptyObjects bool) common.Hash {
for addr := range s.stateObjectsPending {
if obj := s.stateObjects[addr]; obj.deleted {
s.deleteStateObject(obj)
+ s.AccountDeleted += 1
} else {
s.updateStateObject(obj)
+ s.AccountUpdated += 1
}
usedAddrs = append(usedAddrs, common.CopyBytes(addr[:])) // Copy needed for closure
}
@@ -903,6 +905,7 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
s.IntermediateRoot(deleteEmptyObjects)
// Commit objects to the trie, measuring the elapsed time
+ var storageCommitted int
codeWriter := s.db.TrieDB().DiskDB().NewBatch()
for addr := range s.stateObjectsDirty {
if obj := s.stateObjects[addr]; !obj.deleted {
@@ -912,9 +915,11 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
obj.dirtyCode = false
}
// Write any storage changes in the state object to its storage trie
- if err := obj.CommitTrie(s.db); err != nil {
+ committed, err := obj.CommitTrie(s.db)
+ if err != nil {
return common.Hash{}, err
}
+ storageCommitted += committed
}
}
if len(s.stateObjectsDirty) > 0 {
@@ -932,8 +937,8 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
}
// The onleaf func is called _serially_, so we can reuse the same account
// for unmarshalling every time.
- var account Account
- root, err := s.trie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error {
+ var account types.StateAccount
+ root, accountCommitted, err := s.trie.Commit(func(_ [][]byte, _ []byte, leaf []byte, parent common.Hash) error {
if err := rlp.DecodeBytes(leaf, &account); err != nil {
return nil
}
@@ -942,8 +947,20 @@ func (s *StateDB) Commit(deleteEmptyObjects bool) (common.Hash, error) {
}
return nil
})
+ if err != nil {
+ return common.Hash{}, err
+ }
if metrics.EnabledExpensive {
s.AccountCommits += time.Since(start)
+
+ accountUpdatedMeter.Mark(int64(s.AccountUpdated))
+ storageUpdatedMeter.Mark(int64(s.StorageUpdated))
+ accountDeletedMeter.Mark(int64(s.AccountDeleted))
+ storageDeletedMeter.Mark(int64(s.StorageDeleted))
+ accountCommittedMeter.Mark(int64(accountCommitted))
+ storageCommittedMeter.Mark(int64(storageCommitted))
+ s.AccountUpdated, s.AccountDeleted = 0, 0
+ s.StorageUpdated, s.StorageDeleted = 0, 0
}
// If snapshotting is enabled, update the snapshot tree with this new version
if s.snap != nil {
diff --git a/core/state/sync.go b/core/state/sync.go
index 7a5852fb19..734961d9c5 100644
--- a/core/state/sync.go
+++ b/core/state/sync.go
@@ -20,6 +20,7 @@ import (
"bytes"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
@@ -43,7 +44,7 @@ func NewStateSync(root common.Hash, database ethdb.KeyValueReader, bloom *trie.S
return err
}
}
- var obj Account
+ var obj types.StateAccount
if err := rlp.Decode(bytes.NewReader(leaf), &obj); err != nil {
return err
}
diff --git a/core/state/sync_test.go b/core/state/sync_test.go
index a13fcf56a3..beb8fcfd9c 100644
--- a/core/state/sync_test.go
+++ b/core/state/sync_test.go
@@ -23,6 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethdb/memorydb"
@@ -203,7 +204,7 @@ func testIterativeStateSync(t *testing.T, count int, commit bool, bypath bool) {
}
results[len(hashQueue)+i] = trie.SyncResult{Hash: crypto.Keccak256Hash(data), Data: data}
} else {
- var acc Account
+ var acc types.StateAccount
if err := rlp.DecodeBytes(srcTrie.Get(path[0]), &acc); err != nil {
t.Fatalf("failed to decode account on path %x: %v", path, err)
}
diff --git a/core/tx_list.go b/core/tx_list.go
index 607838ba37..f141a03bbd 100644
--- a/core/tx_list.go
+++ b/core/tx_list.go
@@ -21,6 +21,8 @@ import (
"math"
"math/big"
"sort"
+ "sync"
+ "sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
@@ -293,9 +295,9 @@ func (l *txList) Add(tx *types.Transaction, priceBump uint64) (bool, *types.Tran
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
+ // We have to ensure that both the new fee cap and tip are 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
+ // this is accurate for low (Wei-level) gas price replacements.
if tx.GasFeeCapIntCmp(thresholdFeeCap) < 0 || tx.GasTipCapIntCmp(thresholdTip) < 0 {
return false, nil
}
@@ -478,9 +480,15 @@ func (h *priceHeap) Pop() interface{} {
// 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 txPricedList struct {
- all *txLookup // Pointer to the map of all transactions
- urgent, floating priceHeap // Heaps of prices of all the stored **remote** transactions
- stales int // Number of stale price points to (re-heap trigger)
+ // Number of stale price points to (re-heap trigger).
+ // This field is accessed atomically, and must be the first field
+ // to ensure it has correct alignment for atomic.AddInt64.
+ // See https://golang.org/pkg/sync/atomic/#pkg-note-BUG.
+ stales int64
+
+ all *txLookup // Pointer to the map of all transactions
+ urgent, floating priceHeap // Heaps of prices of all the stored **remote** transactions
+ reheapMu sync.Mutex // Mutex asserts that only one routine is reheaping the list
}
const (
@@ -510,8 +518,8 @@ func (l *txPricedList) Put(tx *types.Transaction, local bool) {
// the heap if a large enough ratio of transactions go stale.
func (l *txPricedList) Removed(count int) {
// Bump the stale counter, but exit if still too low (< 25%)
- l.stales += count
- if l.stales <= (len(l.urgent.list)+len(l.floating.list))/4 {
+ stales := atomic.AddInt64(&l.stales, int64(count))
+ if int(stales) <= (len(l.urgent.list)+len(l.floating.list))/4 {
return
}
// Seems we've reached a critical number of stale transactions, reheap
@@ -535,7 +543,7 @@ func (l *txPricedList) underpricedFor(h *priceHeap, tx *types.Transaction) bool
for len(h.list) > 0 {
head := h.list[0]
if l.all.GetRemote(head.Hash()) == nil { // Removed or migrated
- l.stales--
+ atomic.AddInt64(&l.stales, -1)
heap.Pop(h)
continue
}
@@ -561,7 +569,7 @@ func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool)
// Discard stale transactions if found during cleanup
tx := heap.Pop(&l.urgent).(*types.Transaction)
if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated
- l.stales--
+ atomic.AddInt64(&l.stales, -1)
continue
}
// Non stale transaction found, move to floating heap
@@ -574,7 +582,7 @@ func (l *txPricedList) Discard(slots int, force bool) (types.Transactions, bool)
// Discard stale transactions if found during cleanup
tx := heap.Pop(&l.floating).(*types.Transaction)
if l.all.GetRemote(tx.Hash()) == nil { // Removed or migrated
- l.stales--
+ atomic.AddInt64(&l.stales, -1)
continue
}
// Non stale transaction found, discard it
@@ -594,8 +602,10 @@ 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() {
+ l.reheapMu.Lock()
+ defer l.reheapMu.Unlock()
start := time.Now()
- l.stales = 0
+ atomic.StoreInt64(&l.stales, 0)
l.urgent.list = make([]*types.Transaction, 0, l.all.RemoteCount())
l.all.Range(func(hash common.Hash, tx *types.Transaction, local bool) bool {
l.urgent.list = append(l.urgent.list, tx)
diff --git a/core/tx_list_test.go b/core/tx_list_test.go
index 3a5842d2e8..ef49cae1dd 100644
--- a/core/tx_list_test.go
+++ b/core/tx_list_test.go
@@ -51,7 +51,7 @@ func TestStrictTxListAdd(t *testing.T) {
}
}
-func BenchmarkTxListAdd(t *testing.B) {
+func BenchmarkTxListAdd(b *testing.B) {
// Generate a list of transactions to insert
key, _ := crypto.GenerateKey()
@@ -60,11 +60,13 @@ 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))
- t.ResetTimer()
- for _, v := range rand.Perm(len(txs)) {
- list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
- list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump)
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ list := newTxList(true)
+ for _, v := range rand.Perm(len(txs)) {
+ list.Add(txs[v], DefaultTxPoolConfig.PriceBump)
+ list.Filter(priceLimit, DefaultTxPoolConfig.PriceBump)
+ }
}
}
diff --git a/core/tx_pool.go b/core/tx_pool.go
index f6228df2fa..4f627fcb9e 100644
--- a/core/tx_pool.go
+++ b/core/tx_pool.go
@@ -22,6 +22,7 @@ import (
"math/big"
"sort"
"sync"
+ "sync/atomic"
"time"
"github.com/ethereum/go-ethereum/common"
@@ -111,6 +112,14 @@ var (
invalidTxMeter = metrics.NewRegisteredMeter("txpool/invalid", nil)
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)
@@ -256,6 +265,9 @@ type TxPool struct {
reorgDoneCh chan chan struct{}
reorgShutdownCh chan struct{} // requests shutdown of scheduleReorgLoop
wg sync.WaitGroup // tracks loop, scheduleReorgLoop
+ initDoneCh chan struct{} // is closed once the pool is initialized (for tests)
+
+ changesSinceReorg int // A counter for how many drops we've performed in-between reorg.
}
type txpoolResetRequest struct {
@@ -284,6 +296,7 @@ func NewTxPool(config TxPoolConfig, chainconfig *params.ChainConfig, chain block
queueTxEventCh: make(chan *types.Transaction),
reorgDoneCh: make(chan chan struct{}),
reorgShutdownCh: make(chan struct{}),
+ initDoneCh: make(chan struct{}),
gasPrice: new(big.Int).SetUint64(config.PriceLimit),
}
pool.locals = newAccountSet(pool.signer)
@@ -337,6 +350,8 @@ func (pool *TxPool) loop() {
defer evict.Stop()
defer journal.Stop()
+ // Notify tests that the init phase is done
+ close(pool.initDoneCh)
for {
select {
// Handle ChainHeadEvent
@@ -355,8 +370,8 @@ func (pool *TxPool) loop() {
case <-report.C:
pool.mu.RLock()
pending, queued := pool.stats()
- stales := pool.priced.stales
pool.mu.RUnlock()
+ stales := int(atomic.LoadInt64(&pool.priced.stales))
if pending != prevPending || queued != prevQueued || stales != prevStales {
log.Debug("Transaction pool status report", "executable", pending, "queued", queued, "stales", stales)
@@ -518,7 +533,7 @@ func (pool *TxPool) ContentFrom(addr common.Address) (types.Transactions, types.
// 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, error) {
+func (pool *TxPool) Pending(enforceTips bool) map[common.Address]types.Transactions {
pool.mu.Lock()
defer pool.mu.Unlock()
@@ -539,7 +554,7 @@ func (pool *TxPool) Pending(enforceTips bool) (map[common.Address]types.Transact
pending[addr] = txs
}
}
- return pending, nil
+ return pending
}
// Locals retrieves the accounts currently considered local by the pool.
@@ -663,6 +678,15 @@ func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err e
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.
@@ -674,6 +698,8 @@ 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(), "gasTipCap", tx.GasTipCap(), "gasFeeCap", tx.GasFeeCap())
@@ -1114,6 +1140,9 @@ 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) {
+ defer func(t0 time.Time) {
+ reorgDurationTimer.Update(time.Since(t0))
+ }(time.Now())
defer close(done)
var promoteAddrs []common.Address
@@ -1163,6 +1192,8 @@ func (pool *TxPool) runReorg(done chan struct{}, reset *txpoolResetRequest, dirt
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
diff --git a/core/tx_pool_test.go b/core/tx_pool_test.go
index e02096fe25..1406c8df02 100644
--- a/core/tx_pool_test.go
+++ b/core/tx_pool_test.go
@@ -24,6 +24,7 @@ import (
"math/big"
"math/rand"
"os"
+ "sync/atomic"
"testing"
"time"
@@ -57,14 +58,14 @@ func init() {
}
type testBlockChain struct {
+ gasLimit uint64 // must be first field for 64 bit alignment (atomic access)
statedb *state.StateDB
- gasLimit uint64
chainHeadFeed *event.Feed
}
func (bc *testBlockChain) CurrentBlock() *types.Block {
return types.NewBlock(&types.Header{
- GasLimit: bc.gasLimit,
+ GasLimit: atomic.LoadUint64(&bc.gasLimit),
}, nil, nil, nil, trie.NewStackTrie(nil))
}
@@ -118,11 +119,13 @@ func setupTxPool() (*TxPool, *ecdsa.PrivateKey) {
func setupTxPoolWithConfig(config *params.ChainConfig) (*TxPool, *ecdsa.PrivateKey) {
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- blockchain := &testBlockChain{statedb, 10000000, new(event.Feed)}
+ blockchain := &testBlockChain{10000000, statedb, new(event.Feed)}
key, _ := crypto.GenerateKey()
pool := NewTxPool(testTxPoolConfig, config, blockchain)
+ // wait for the pool to initialize
+ <-pool.initDoneCh
return pool, key
}
@@ -228,7 +231,7 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) {
// setup pool with 2 transaction in it
statedb.SetBalance(address, new(big.Int).SetUint64(params.Ether))
- blockchain := &testChain{&testBlockChain{statedb, 1000000000, new(event.Feed)}, address, &trigger}
+ blockchain := &testChain{&testBlockChain{1000000000, statedb, new(event.Feed)}, address, &trigger}
tx0 := transaction(0, 100000, key)
tx1 := transaction(1, 100000, key)
@@ -252,10 +255,6 @@ func TestStateChangeDuringTransactionPoolReset(t *testing.T) {
trigger = true
<-pool.requestReset(nil, nil)
- _, err := pool.Pending(false)
- 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)
@@ -426,7 +425,7 @@ func TestTransactionChainFork(t *testing.T) {
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
statedb.AddBalance(addr, big.NewInt(100000000000000))
- pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
+ pool.chain = &testBlockChain{1000000, statedb, new(event.Feed)}
<-pool.requestReset(nil, nil)
}
resetState()
@@ -455,7 +454,7 @@ func TestTransactionDoubleNonce(t *testing.T) {
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
statedb.AddBalance(addr, big.NewInt(100000000000000))
- pool.chain = &testBlockChain{statedb, 1000000, new(event.Feed)}
+ pool.chain = &testBlockChain{1000000, statedb, new(event.Feed)}
<-pool.requestReset(nil, nil)
}
resetState()
@@ -625,7 +624,7 @@ func TestTransactionDropping(t *testing.T) {
t.Errorf("total transaction mismatch: have %d, want %d", pool.all.Count(), 4)
}
// Reduce the block gas limit, check that invalidated transactions are dropped
- pool.chain.(*testBlockChain).gasLimit = 100
+ atomic.StoreUint64(&pool.chain.(*testBlockChain).gasLimit, 100)
<-pool.requestReset(nil, nil)
if _, ok := pool.pending[account].txs.items[tx0.Nonce()]; !ok {
@@ -653,7 +652,7 @@ func TestTransactionPostponing(t *testing.T) {
// Create the pool to test the postponing with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+ blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
defer pool.Stop()
@@ -866,7 +865,7 @@ func testTransactionQueueGlobalLimiting(t *testing.T, nolocals bool) {
// Create the pool to test the limit enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+ blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
config := testTxPoolConfig
config.NoLocals = nolocals
@@ -958,7 +957,7 @@ func testTransactionQueueTimeLimiting(t *testing.T, nolocals bool) {
// Create the pool to test the non-expiration enforcement
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+ blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
config := testTxPoolConfig
config.Lifetime = time.Second
@@ -1143,7 +1142,7 @@ func TestTransactionPendingGlobalLimiting(t *testing.T) {
// Create the pool to test the limit enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+ blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
config := testTxPoolConfig
config.GlobalSlots = config.AccountSlots * 10
@@ -1245,7 +1244,7 @@ func TestTransactionCapClearsFromAll(t *testing.T) {
// Create the pool to test the limit enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+ blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
config := testTxPoolConfig
config.AccountSlots = 2
@@ -1279,7 +1278,7 @@ func TestTransactionPendingMinimumAllowance(t *testing.T) {
// Create the pool to test the limit enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+ blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
config := testTxPoolConfig
config.GlobalSlots = 1
@@ -1327,7 +1326,7 @@ func TestTransactionPoolRepricing(t *testing.T) {
// Create the pool to test the pricing enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+ blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
defer pool.Stop()
@@ -1575,7 +1574,7 @@ func TestTransactionPoolRepricingKeepsLocals(t *testing.T) {
// Create the pool to test the pricing enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+ blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
pool := NewTxPool(testTxPoolConfig, eip1559Config, blockchain)
defer pool.Stop()
@@ -1648,7 +1647,7 @@ func TestTransactionPoolUnderpricing(t *testing.T) {
// Create the pool to test the pricing enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+ blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
config := testTxPoolConfig
config.GlobalSlots = 2
@@ -1754,7 +1753,7 @@ func TestTransactionPoolStableUnderpricing(t *testing.T) {
// Create the pool to test the pricing enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+ blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
config := testTxPoolConfig
config.GlobalSlots = 128
@@ -1946,20 +1945,20 @@ func TestDualHeapEviction(t *testing.T) {
}
add := func(urgent bool) {
- txs := make([]*types.Transaction, 20)
- for i := range txs {
+ 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 {
- txs[i] = dynamicFeeTx(0, 100000, big.NewInt(int64(baseFee+1+i)), big.NewInt(int64(1+i)), key)
- highTip = txs[i]
+ tx = dynamicFeeTx(0, 100000, big.NewInt(int64(baseFee+1+i)), big.NewInt(int64(1+i)), key)
+ highTip = tx
} else {
- txs[i] = dynamicFeeTx(0, 100000, big.NewInt(int64(baseFee+200+i)), big.NewInt(1), key)
- highCap = txs[i]
+ tx = dynamicFeeTx(0, 100000, big.NewInt(int64(baseFee+200+i)), big.NewInt(1), key)
+ highCap = tx
}
+ pool.AddRemotesSync([]*types.Transaction{tx})
}
- pool.AddRemotes(txs)
pending, queued := pool.Stats()
if pending+queued != 20 {
t.Fatalf("transaction count mismatch: have %d, want %d", pending+queued, 10)
@@ -1986,7 +1985,7 @@ func TestTransactionDeduplication(t *testing.T) {
// Create the pool to test the pricing enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+ blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
defer pool.Stop()
@@ -2052,7 +2051,7 @@ func TestTransactionReplacement(t *testing.T) {
// Create the pool to test the pricing enforcement with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+ blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
defer pool.Stop()
@@ -2257,7 +2256,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
// Create the original pool to inject transaction into the journal
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+ blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
config := testTxPoolConfig
config.NoLocals = nolocals
@@ -2299,7 +2298,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
// Terminate the old pool, bump the local nonce, create a new pool and ensure relevant transaction survive
pool.Stop()
statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1)
- blockchain = &testBlockChain{statedb, 1000000, new(event.Feed)}
+ blockchain = &testBlockChain{1000000, statedb, new(event.Feed)}
pool = NewTxPool(config, params.TestChainConfig, blockchain)
@@ -2326,7 +2325,7 @@ func testTransactionJournaling(t *testing.T, nolocals bool) {
pool.Stop()
statedb.SetNonce(crypto.PubkeyToAddress(local.PublicKey), 1)
- blockchain = &testBlockChain{statedb, 1000000, new(event.Feed)}
+ blockchain = &testBlockChain{1000000, statedb, new(event.Feed)}
pool = NewTxPool(config, params.TestChainConfig, blockchain)
pending, queued = pool.Stats()
@@ -2355,7 +2354,7 @@ func TestTransactionStatusCheck(t *testing.T) {
// Create the pool to test the status retrievals with
statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
- blockchain := &testBlockChain{statedb, 1000000, new(event.Feed)}
+ blockchain := &testBlockChain{1000000, statedb, new(event.Feed)}
pool := NewTxPool(testTxPoolConfig, params.TestChainConfig, blockchain)
defer pool.Stop()
diff --git a/core/types/access_list_tx.go b/core/types/access_list_tx.go
index d8d08b48c5..ee5f194b77 100644
--- a/core/types/access_list_tx.go
+++ b/core/types/access_list_tx.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.
@@ -96,7 +96,6 @@ 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 }
diff --git a/core/types/dynamic_fee_tx.go b/core/types/dynamic_fee_tx.go
index c6719a4089..585c029d89 100644
--- a/core/types/dynamic_fee_tx.go
+++ b/core/types/dynamic_fee_tx.go
@@ -43,7 +43,7 @@ type DynamicFeeTx struct {
func (tx *DynamicFeeTx) copy() TxData {
cpy := &DynamicFeeTx{
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.
@@ -84,7 +84,6 @@ func (tx *DynamicFeeTx) copy() TxData {
// accessors for innerTx.
func (tx *DynamicFeeTx) txType() byte { return DynamicFeeTxType }
func (tx *DynamicFeeTx) chainID() *big.Int { return tx.ChainID }
-func (tx *DynamicFeeTx) protected() bool { return true }
func (tx *DynamicFeeTx) accessList() AccessList { return tx.AccessList }
func (tx *DynamicFeeTx) data() []byte { return tx.Data }
func (tx *DynamicFeeTx) gas() uint64 { return tx.Gas }
diff --git a/core/types/legacy_tx.go b/core/types/legacy_tx.go
index 514010ebbd..cb86bed772 100644
--- a/core/types/legacy_tx.go
+++ b/core/types/legacy_tx.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.
diff --git a/core/types/receipt.go b/core/types/receipt.go
index 6141740f2f..c3588990c0 100644
--- a/core/types/receipt.go
+++ b/core/types/receipt.go
@@ -144,13 +144,29 @@ func (r *Receipt) EncodeRLP(w io.Writer) error {
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 {
@@ -189,6 +205,42 @@ func (r *Receipt) DecodeRLP(s *rlp.Stream) error {
}
}
+// 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) == 0 {
+ return errEmptyTypedReceipt
+ }
+ switch b[0] {
+ case DynamicFeeTxType, AccessListTxType:
+ var data receiptRLP
+ err := rlp.DecodeBytes(b[1:], &data)
+ if err != nil {
+ return err
+ }
+ r.Type = b[0]
+ return r.setFromRLP(data)
+ default:
+ return ErrTxTypeNotSupported
+ }
+}
+
func (r *Receipt) setFromRLP(data receiptRLP) error {
r.CumulativeGasUsed, r.Bloom, r.Logs = data.CumulativeGasUsed, data.Bloom, data.Logs
return r.setStatus(data.PostStateOrStatus)
@@ -354,42 +406,42 @@ func (rs Receipts) EncodeIndex(i int, w *bytes.Buffer) {
// 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, txs Transactions) 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()
// 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())
}
// 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 22a316c237..613559a658 100644
--- a/core/types/receipt_test.go
+++ b/core/types/receipt_test.go
@@ -29,6 +29,59 @@ import (
"github.com/ethereum/go-ethereum/rlp"
)
+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,
+ }
+)
+
func TestDecodeEmptyTypedReceipt(t *testing.T) {
input := []byte{0x80}
var r Receipt
@@ -273,9 +326,6 @@ func TestDeriveFields(t *testing.T) {
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].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)
}
@@ -315,6 +365,105 @@ func TestTypedReceiptEncodingDecoding(t *testing.T) {
}
}
+func TestReceiptMarshalBinary(t *testing.T) {
+ // Legacy Receipt
+ legacyReceipt.Bloom = CreateBloom(Receipts{legacyReceipt})
+ have, err := legacyReceipt.MarshalBinary()
+ if err != nil {
+ t.Fatalf("marshal binary error: %v", err)
+ }
+ legacyReceipts := Receipts{legacyReceipt}
+ buf := new(bytes.Buffer)
+ legacyReceipts.EncodeIndex(0, buf)
+ haveEncodeIndex := buf.Bytes()
+ if !bytes.Equal(have, haveEncodeIndex) {
+ t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex)
+ }
+ buf.Reset()
+ if err := legacyReceipt.EncodeRLP(buf); err != nil {
+ t.Fatalf("encode rlp error: %v", err)
+ }
+ haveRLPEncode := buf.Bytes()
+ if !bytes.Equal(have, haveRLPEncode) {
+ t.Errorf("BinaryMarshal and EncodeRLP mismatch for legacy tx, got %x want %x", have, haveRLPEncode)
+ }
+ legacyWant := common.FromHex("f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
+ if !bytes.Equal(have, legacyWant) {
+ t.Errorf("encoded RLP mismatch, got %x want %x", have, legacyWant)
+ }
+
+ // 2930 Receipt
+ buf.Reset()
+ accessListReceipt.Bloom = CreateBloom(Receipts{accessListReceipt})
+ have, err = accessListReceipt.MarshalBinary()
+ if err != nil {
+ t.Fatalf("marshal binary error: %v", err)
+ }
+ accessListReceipts := Receipts{accessListReceipt}
+ accessListReceipts.EncodeIndex(0, buf)
+ haveEncodeIndex = buf.Bytes()
+ if !bytes.Equal(have, haveEncodeIndex) {
+ t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex)
+ }
+ accessListWant := common.FromHex("01f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
+ if !bytes.Equal(have, accessListWant) {
+ t.Errorf("encoded RLP mismatch, got %x want %x", have, accessListWant)
+ }
+
+ // 1559 Receipt
+ buf.Reset()
+ eip1559Receipt.Bloom = CreateBloom(Receipts{eip1559Receipt})
+ have, err = eip1559Receipt.MarshalBinary()
+ if err != nil {
+ t.Fatalf("marshal binary error: %v", err)
+ }
+ eip1559Receipts := Receipts{eip1559Receipt}
+ eip1559Receipts.EncodeIndex(0, buf)
+ haveEncodeIndex = buf.Bytes()
+ if !bytes.Equal(have, haveEncodeIndex) {
+ t.Errorf("BinaryMarshal and EncodeIndex mismatch, got %x want %x", have, haveEncodeIndex)
+ }
+ eip1559Want := common.FromHex("02f901c58001b9010000000000000010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000000000000000010000080000000000000000000004000000000000000000000000000040000000000000000000000000000800000000000000000000000000000000000000000000000000000400000000000000000000000000000000000000000000000000000000000010000000000000000000000000000000000000000000000000000000000000f8bef85d940000000000000000000000000000000000000011f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100fff85d940000000000000000000000000000000000000111f842a0000000000000000000000000000000000000000000000000000000000000deada0000000000000000000000000000000000000000000000000000000000000beef830100ff")
+ if !bytes.Equal(have, eip1559Want) {
+ t.Errorf("encoded RLP mismatch, got %x want %x", have, eip1559Want)
+ }
+}
+
+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)
+ }
+}
+
func clearComputedFieldsOnReceipts(t *testing.T, receipts Receipts) {
t.Helper()
diff --git a/core/types/state_account.go b/core/types/state_account.go
new file mode 100644
index 0000000000..68804bf311
--- /dev/null
+++ b/core/types/state_account.go
@@ -0,0 +1,32 @@
+// 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/ethereum/go-ethereum/common"
+)
+
+// StateAccount is the Ethereum consensus representation of accounts.
+// These objects are stored in the main account trie.
+type StateAccount struct {
+ Nonce uint64
+ Balance *big.Int
+ Root common.Hash // merkle root of the storage trie
+ CodeHash []byte
+}
diff --git a/core/types/transaction.go b/core/types/transaction.go
index e21cf2bda8..83f1766e67 100644
--- a/core/types/transaction.go
+++ b/core/types/transaction.go
@@ -284,13 +284,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())
}
// Cost returns gas * gasPrice + value.
@@ -632,3 +626,12 @@ func (m Message) Nonce() uint64 { return m.nonce }
func (m Message) Data() []byte { return m.data }
func (m Message) AccessList() AccessList { return m.accessList }
func (m Message) IsFake() bool { return m.isFake }
+
+// copyAddressPtr copies an address.
+func copyAddressPtr(a *common.Address) *common.Address {
+ if a == nil {
+ return nil
+ }
+ cpy := *a
+ return &cpy
+}
diff --git a/core/vm/access_list_tracer.go b/core/vm/access_list_tracer.go
index cc5461d1c3..11b4e29426 100644
--- a/core/vm/access_list_tracer.go
+++ b/core/vm/access_list_tracer.go
@@ -166,6 +166,11 @@ func (*AccessListTracer) CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost
func (*AccessListTracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error) {}
+func (*AccessListTracer) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+}
+
+func (*AccessListTracer) CaptureExit(output []byte, gasUsed uint64, err error) {}
+
// AccessList returns the current accesslist maintained by the tracer.
func (a *AccessListTracer) AccessList() types.AccessList {
return a.list.accessList()
diff --git a/core/vm/analysis_test.go b/core/vm/analysis_test.go
index 585bb3097f..d7f21e04aa 100644
--- a/core/vm/analysis_test.go
+++ b/core/vm/analysis_test.go
@@ -55,9 +55,12 @@ func TestJumpDestAnalysis(t *testing.T) {
}
}
+const analysisCodeSize = 1200 * 1024
+
func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) {
// 1.4 ms
- code := make([]byte, 1200000)
+ code := make([]byte, analysisCodeSize)
+ bench.SetBytes(analysisCodeSize)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
codeBitmap(code)
@@ -66,7 +69,8 @@ func BenchmarkJumpdestAnalysis_1200k(bench *testing.B) {
}
func BenchmarkJumpdestHashing_1200k(bench *testing.B) {
// 4 ms
- code := make([]byte, 1200000)
+ code := make([]byte, analysisCodeSize)
+ bench.SetBytes(analysisCodeSize)
bench.ResetTimer()
for i := 0; i < bench.N; i++ {
crypto.Keccak256Hash(code)
@@ -77,13 +81,19 @@ func BenchmarkJumpdestHashing_1200k(bench *testing.B) {
func BenchmarkJumpdestOpAnalysis(bench *testing.B) {
var op OpCode
bencher := func(b *testing.B) {
- code := make([]byte, 32*b.N)
+ code := make([]byte, analysisCodeSize)
+ b.SetBytes(analysisCodeSize)
for i := range code {
code[i] = byte(op)
}
bits := make(bitvec, len(code)/8+1+4)
b.ResetTimer()
- codeBitmapInternal(code, bits)
+ for i := 0; i < b.N; i++ {
+ for j := range bits {
+ bits[j] = 0
+ }
+ codeBitmapInternal(code, bits)
+ }
}
for op = PUSH1; op <= PUSH32; op++ {
bench.Run(op.String(), bencher)
diff --git a/core/vm/evm.go b/core/vm/evm.go
index 8964766736..07e9619158 100644
--- a/core/vm/evm.go
+++ b/core/vm/evm.go
@@ -182,9 +182,14 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
if !evm.StateDB.Exist(addr) {
if !isPrecompile && evm.chainRules.IsEIP158 && value.Sign() == 0 {
// Calling a non existing account, don't do anything, but ping the tracer
- if evm.Config.Debug && evm.depth == 0 {
- evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
- evm.Config.Tracer.CaptureEnd(ret, 0, 0, nil)
+ if evm.Config.Debug {
+ if evm.depth == 0 {
+ evm.Config.Tracer.CaptureStart(evm, caller.Address(), addr, false, input, gas, value)
+ evm.Config.Tracer.CaptureEnd(ret, 0, 0, nil)
+ } else {
+ evm.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
+ evm.Config.Tracer.CaptureExit(ret, 0, nil)
+ }
}
return nil, gas, nil
}
@@ -193,11 +198,19 @@ func (evm *EVM) Call(caller ContractRef, addr common.Address, input []byte, gas
evm.Context.Transfer(evm.StateDB, caller.Address(), addr, value)
// Capture the tracer start/end events in debug mode
- if evm.Config.Debug && evm.depth == 0 {
- 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.Config.Tracer.CaptureEnd(ret, startGas-gas, time.Since(startTime), err)
- }(gas, time.Now())
+ if evm.Config.Debug {
+ if evm.depth == 0 {
+ 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.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.Config.Tracer.CaptureEnter(CALL, caller.Address(), addr, input, gas, value)
+ defer func(startGas uint64) {
+ evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
+ }(gas)
+ }
}
if isPrecompile {
@@ -257,6 +270,14 @@ func (evm *EVM) CallCode(caller ContractRef, addr common.Address, input []byte,
}
var snapshot = evm.StateDB.Snapshot()
+ // Invoke tracer hooks that signal entering/exiting a call frame
+ if evm.Config.Debug {
+ evm.Config.Tracer.CaptureEnter(CALLCODE, caller.Address(), addr, input, gas, value)
+ defer func(startGas uint64) {
+ evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
+ }(gas)
+ }
+
// It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
@@ -293,6 +314,14 @@ func (evm *EVM) DelegateCall(caller ContractRef, addr common.Address, input []by
}
var snapshot = evm.StateDB.Snapshot()
+ // Invoke tracer hooks that signal entering/exiting a call frame
+ if evm.Config.Debug {
+ evm.Config.Tracer.CaptureEnter(DELEGATECALL, caller.Address(), addr, input, gas, nil)
+ defer func(startGas uint64) {
+ evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
+ }(gas)
+ }
+
// It is allowed to call precompiles, even via delegatecall
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
@@ -338,6 +367,14 @@ func (evm *EVM) StaticCall(caller ContractRef, addr common.Address, input []byte
// future scenarios
evm.StateDB.AddBalance(addr, big0)
+ // Invoke tracer hooks that signal entering/exiting a call frame
+ if evm.Config.Debug {
+ evm.Config.Tracer.CaptureEnter(STATICCALL, caller.Address(), addr, input, gas, nil)
+ defer func(startGas uint64) {
+ evm.Config.Tracer.CaptureExit(ret, startGas-gas, err)
+ }(gas)
+ }
+
if p, isPrecompile := evm.precompile(addr); isPrecompile {
ret, gas, err = RunPrecompiledContract(p, input, gas)
} else {
@@ -377,7 +414,7 @@ func (c *codeAndHash) Hash() common.Hash {
}
// create creates a new contract using code as deployment code.
-func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address) ([]byte, common.Address, uint64, error) {
+func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64, value *big.Int, address common.Address, typ OpCode) ([]byte, common.Address, uint64, error) {
// Depth check execution. Fail if we're trying to execute above the
// limit.
if evm.depth > int(params.CallCreateDepth) {
@@ -415,9 +452,14 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
return nil, address, gas, nil
}
- if evm.Config.Debug && evm.depth == 0 {
- evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
+ if evm.Config.Debug {
+ if evm.depth == 0 {
+ evm.Config.Tracer.CaptureStart(evm, caller.Address(), address, true, codeAndHash.code, gas, value)
+ } else {
+ evm.Config.Tracer.CaptureEnter(typ, caller.Address(), address, codeAndHash.code, gas, value)
+ }
}
+
start := time.Now()
ret, err := evm.interpreter.Run(contract, nil, false)
@@ -455,8 +497,12 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
}
}
- if evm.Config.Debug && evm.depth == 0 {
- evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
+ if evm.Config.Debug {
+ if evm.depth == 0 {
+ evm.Config.Tracer.CaptureEnd(ret, gas-contract.Gas, time.Since(start), err)
+ } else {
+ evm.Config.Tracer.CaptureExit(ret, gas-contract.Gas, err)
+ }
}
return ret, address, contract.Gas, err
}
@@ -464,7 +510,7 @@ func (evm *EVM) create(caller ContractRef, codeAndHash *codeAndHash, gas uint64,
// Create creates a new contract using code as deployment code.
func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.Int) (ret []byte, contractAddr common.Address, leftOverGas uint64, err error) {
contractAddr = crypto.CreateAddress(caller.Address(), evm.StateDB.GetNonce(caller.Address()))
- return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr)
+ return evm.create(caller, &codeAndHash{code: code}, gas, value, contractAddr, CREATE)
}
// Create2 creates a new contract using code as deployment code.
@@ -474,7 +520,7 @@ func (evm *EVM) Create(caller ContractRef, code []byte, gas uint64, value *big.I
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(), salt.Bytes32(), codeAndHash.Hash().Bytes())
- return evm.create(caller, codeAndHash, gas, endowment, contractAddr)
+ return evm.create(caller, codeAndHash, gas, endowment, contractAddr, CREATE2)
}
// ChainConfig returns the environment's chain configuration
diff --git a/core/vm/instructions.go b/core/vm/instructions.go
index 6c8c6e6e6f..bda480f083 100644
--- a/core/vm/instructions.go
+++ b/core/vm/instructions.go
@@ -791,6 +791,10 @@ func opSuicide(pc *uint64, interpreter *EVMInterpreter, scope *ScopeContext) ([]
balance := interpreter.evm.StateDB.GetBalance(scope.Contract.Address())
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)
+ }
return nil, nil
}
diff --git a/core/vm/logger.go b/core/vm/logger.go
index 900a5e5854..52dc0b8a0e 100644
--- a/core/vm/logger.go
+++ b/core/vm/logger.go
@@ -46,12 +46,12 @@ func (s Storage) Copy() Storage {
// LogConfig are the configuration options for structured logger the EVM
type LogConfig struct {
- DisableMemory bool // disable memory capture
- DisableStack bool // disable stack capture
- DisableStorage bool // disable storage capture
- DisableReturnData bool // disable return data capture
- Debug bool // print output during capture end
- Limit int // maximum length of output, but zero means unlimited
+ EnableMemory bool // enable memory capture
+ DisableStack bool // disable stack capture
+ DisableStorage bool // disable storage capture
+ EnableReturnData bool // enable return data capture
+ Debug bool // print output during capture end
+ Limit int // maximum length of output, but zero means unlimited
// Chain overrides, can be used to execute a trace using future fork rules
Overrides *params.ChainConfig `json:"overrides,omitempty"`
}
@@ -106,6 +106,8 @@ func (s *StructLog) ErrorString() string {
type Tracer interface {
CaptureStart(env *EVM, from common.Address, to common.Address, create bool, input []byte, gas uint64, value *big.Int)
CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, rData []byte, depth int, err error)
+ CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int)
+ CaptureExit(output []byte, gasUsed uint64, err error)
CaptureFault(env *EVM, pc uint64, op OpCode, gas, cost uint64, scope *ScopeContext, depth int, err error)
CaptureEnd(output []byte, gasUsed uint64, t time.Duration, err error)
}
@@ -160,7 +162,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
}
// Copy a snapshot of the current memory state to a new buffer
var mem []byte
- if !l.cfg.DisableMemory {
+ if l.cfg.EnableMemory {
mem = make([]byte, len(memory.Data()))
copy(mem, memory.Data())
}
@@ -199,7 +201,7 @@ func (l *StructLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost ui
}
}
var rdata []byte
- if !l.cfg.DisableReturnData {
+ if l.cfg.EnableReturnData {
rdata = make([]byte, len(rData))
copy(rdata, rData)
}
@@ -225,6 +227,11 @@ func (l *StructLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration
}
}
+func (l *StructLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+}
+
+func (l *StructLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
+
// StructLogs returns the captured log entries.
func (l *StructLogger) StructLogs() []StructLog { return l.logs }
@@ -342,3 +349,8 @@ func (t *mdLogger) CaptureEnd(output []byte, gasUsed uint64, tm time.Duration, e
fmt.Fprintf(t.out, "\nOutput: `0x%x`\nConsumed gas: `%d`\nError: `%v`\n",
output, gasUsed, err)
}
+
+func (t *mdLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+}
+
+func (t *mdLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
diff --git a/core/vm/logger_json.go b/core/vm/logger_json.go
index 5210f479fe..479a00c0ac 100644
--- a/core/vm/logger_json.go
+++ b/core/vm/logger_json.go
@@ -61,13 +61,13 @@ func (l *JSONLogger) CaptureState(env *EVM, pc uint64, op OpCode, gas, cost uint
RefundCounter: env.StateDB.GetRefund(),
Err: err,
}
- if !l.cfg.DisableMemory {
+ if l.cfg.EnableMemory {
log.Memory = memory.Data()
}
if !l.cfg.DisableStack {
log.Stack = stack.data
}
- if !l.cfg.DisableReturnData {
+ if l.cfg.EnableReturnData {
log.ReturnData = rData
}
l.encoder.Encode(log)
@@ -87,3 +87,8 @@ func (l *JSONLogger) CaptureEnd(output []byte, gasUsed uint64, t time.Duration,
}
l.encoder.Encode(endLog{common.Bytes2Hex(output), math.HexOrDecimal64(gasUsed), t, errMsg})
}
+
+func (l *JSONLogger) CaptureEnter(typ OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+}
+
+func (l *JSONLogger) CaptureExit(output []byte, gasUsed uint64, err error) {}
diff --git a/core/vm/runtime/runtime_test.go b/core/vm/runtime/runtime_test.go
index dcf2d0d447..9f4bafbc7f 100644
--- a/core/vm/runtime/runtime_test.go
+++ b/core/vm/runtime/runtime_test.go
@@ -33,6 +33,7 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
+ "github.com/ethereum/go-ethereum/eth/tracers"
"github.com/ethereum/go-ethereum/params"
)
@@ -342,11 +343,21 @@ func (s *stepCounter) CaptureState(env *vm.EVM, pc uint64, op vm.OpCode, gas, co
// benchmarkNonModifyingCode benchmarks code, but if the code modifies the
// state, this should not be used, since it does not reset the state between runs.
-func benchmarkNonModifyingCode(gas uint64, code []byte, name string, b *testing.B) {
+func benchmarkNonModifyingCode(gas uint64, code []byte, name string, tracerCode string, b *testing.B) {
cfg := new(Config)
setDefaults(cfg)
cfg.State, _ = state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
cfg.GasLimit = gas
+ if len(tracerCode) > 0 {
+ tracer, err := tracers.New(tracerCode, new(tracers.Context))
+ if err != nil {
+ b.Fatal(err)
+ }
+ cfg.EVMConfig = vm.Config{
+ Debug: true,
+ Tracer: tracer,
+ }
+ }
var (
destination = common.BytesToAddress([]byte("contract"))
vmenv = NewEnv(cfg)
@@ -486,12 +497,12 @@ func BenchmarkSimpleLoop(b *testing.B) {
// Tracer: tracer,
// }})
// 100M gas
- benchmarkNonModifyingCode(100000000, staticCallIdentity, "staticcall-identity-100M", b)
- benchmarkNonModifyingCode(100000000, callIdentity, "call-identity-100M", 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, staticCallIdentity, "staticcall-identity-100M", "", b)
+ benchmarkNonModifyingCode(100000000, callIdentity, "call-identity-100M", "", 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(10000000, staticCallIdentity, "staticcall-identity-10M", b)
//benchmarkNonModifyingCode(10000000, loopingCode, "loop-10M", b)
@@ -688,3 +699,241 @@ func TestColdAccountAccessCost(t *testing.T) {
}
}
}
+
+func TestRuntimeJSTracer(t *testing.T) {
+ jsTracers := []string{
+ `{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, steps:0,
+ step: function() { this.steps++},
+ fault: function() {},
+ result: function() {
+ return [this.enters, this.exits,this.enterGas,this.gasUsed, this.steps].join(",")
+ },
+ enter: function(frame) {
+ this.enters++;
+ this.enterGas = frame.getGas();
+ },
+ exit: function(res) {
+ this.exits++;
+ this.gasUsed = res.getGasUsed();
+ }}`,
+ `{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, steps:0,
+ fault: function() {},
+ result: function() {
+ return [this.enters, this.exits,this.enterGas,this.gasUsed, this.steps].join(",")
+ },
+ enter: function(frame) {
+ this.enters++;
+ this.enterGas = frame.getGas();
+ },
+ exit: function(res) {
+ this.exits++;
+ this.gasUsed = res.getGasUsed();
+ }}`}
+ tests := []struct {
+ code []byte
+ // One result per tracer
+ results []string
+ }{
+ {
+ // CREATE
+ code: []byte{
+ // Store initcode in memory at 0x00 (5 bytes left-padded to 32 bytes)
+ byte(vm.PUSH5),
+ // Init code: PUSH1 0, PUSH1 0, RETURN (3 steps)
+ byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN),
+ byte(vm.PUSH1), 0,
+ byte(vm.MSTORE),
+ // length, offset, value
+ byte(vm.PUSH1), 5, byte(vm.PUSH1), 27, byte(vm.PUSH1), 0,
+ byte(vm.CREATE),
+ byte(vm.POP),
+ },
+ results: []string{`"1,1,4294935775,6,12"`, `"1,1,4294935775,6,0"`},
+ },
+ {
+ // CREATE2
+ code: []byte{
+ // Store initcode in memory at 0x00 (5 bytes left-padded to 32 bytes)
+ byte(vm.PUSH5),
+ // Init code: PUSH1 0, PUSH1 0, RETURN (3 steps)
+ byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN),
+ byte(vm.PUSH1), 0,
+ byte(vm.MSTORE),
+ // salt, length, offset, value
+ byte(vm.PUSH1), 1, byte(vm.PUSH1), 5, byte(vm.PUSH1), 27, byte(vm.PUSH1), 0,
+ byte(vm.CREATE2),
+ byte(vm.POP),
+ },
+ results: []string{`"1,1,4294935766,6,13"`, `"1,1,4294935766,6,0"`},
+ },
+ {
+ // CALL
+ code: []byte{
+ // outsize, outoffset, insize, inoffset
+ byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0,
+ byte(vm.PUSH1), 0, // value
+ byte(vm.PUSH1), 0xbb, //address
+ byte(vm.GAS), // gas
+ byte(vm.CALL),
+ byte(vm.POP),
+ },
+ results: []string{`"1,1,4294964716,6,13"`, `"1,1,4294964716,6,0"`},
+ },
+ {
+ // CALLCODE
+ code: []byte{
+ // outsize, outoffset, insize, inoffset
+ byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0,
+ byte(vm.PUSH1), 0, // value
+ byte(vm.PUSH1), 0xcc, //address
+ byte(vm.GAS), // gas
+ byte(vm.CALLCODE),
+ byte(vm.POP),
+ },
+ results: []string{`"1,1,4294964716,6,13"`, `"1,1,4294964716,6,0"`},
+ },
+ {
+ // STATICCALL
+ code: []byte{
+ // outsize, outoffset, insize, inoffset
+ byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0,
+ byte(vm.PUSH1), 0xdd, //address
+ byte(vm.GAS), // gas
+ byte(vm.STATICCALL),
+ byte(vm.POP),
+ },
+ results: []string{`"1,1,4294964719,6,12"`, `"1,1,4294964719,6,0"`},
+ },
+ {
+ // DELEGATECALL
+ code: []byte{
+ // outsize, outoffset, insize, inoffset
+ byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0,
+ byte(vm.PUSH1), 0xee, //address
+ byte(vm.GAS), // gas
+ byte(vm.DELEGATECALL),
+ byte(vm.POP),
+ },
+ results: []string{`"1,1,4294964719,6,12"`, `"1,1,4294964719,6,0"`},
+ },
+ {
+ // CALL self-destructing contract
+ code: []byte{
+ // outsize, outoffset, insize, inoffset
+ byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.PUSH1), 0,
+ byte(vm.PUSH1), 0, // value
+ byte(vm.PUSH1), 0xff, //address
+ byte(vm.GAS), // gas
+ byte(vm.CALL),
+ byte(vm.POP),
+ },
+ results: []string{`"2,2,0,5003,12"`, `"2,2,0,5003,0"`},
+ },
+ }
+ calleeCode := []byte{
+ byte(vm.PUSH1), 0,
+ byte(vm.PUSH1), 0,
+ byte(vm.RETURN),
+ }
+ depressedCode := []byte{
+ byte(vm.PUSH1), 0xaa,
+ byte(vm.SELFDESTRUCT),
+ }
+ main := common.HexToAddress("0xaa")
+ for i, jsTracer := range jsTracers {
+ for j, tc := range tests {
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
+ statedb.SetCode(main, tc.code)
+ statedb.SetCode(common.HexToAddress("0xbb"), calleeCode)
+ statedb.SetCode(common.HexToAddress("0xcc"), calleeCode)
+ statedb.SetCode(common.HexToAddress("0xdd"), calleeCode)
+ statedb.SetCode(common.HexToAddress("0xee"), calleeCode)
+ statedb.SetCode(common.HexToAddress("0xff"), depressedCode)
+
+ tracer, err := tracers.New(jsTracer, new(tracers.Context))
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, _, err = Call(main, nil, &Config{
+ State: statedb,
+ EVMConfig: vm.Config{
+ Debug: true,
+ Tracer: tracer,
+ }})
+ if err != nil {
+ t.Fatal("didn't expect error", err)
+ }
+ res, err := tracer.GetResult()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if have, want := string(res), tc.results[i]; have != want {
+ t.Errorf("wrong result for tracer %d testcase %d, have \n%v\nwant\n%v\n", i, j, have, want)
+ }
+ }
+ }
+}
+
+func TestJSTracerCreateTx(t *testing.T) {
+ jsTracer := `
+ {enters: 0, exits: 0,
+ step: function() {},
+ fault: function() {},
+ result: function() { return [this.enters, this.exits].join(",") },
+ enter: function(frame) { this.enters++ },
+ exit: function(res) { this.exits++ }}`
+ code := []byte{byte(vm.PUSH1), 0, byte(vm.PUSH1), 0, byte(vm.RETURN)}
+
+ statedb, _ := state.New(common.Hash{}, state.NewDatabase(rawdb.NewMemoryDatabase()), nil)
+ tracer, err := tracers.New(jsTracer, new(tracers.Context))
+ if err != nil {
+ t.Fatal(err)
+ }
+ _, _, _, err = Create(code, &Config{
+ State: statedb,
+ EVMConfig: vm.Config{
+ Debug: true,
+ Tracer: tracer,
+ }})
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ res, err := tracer.GetResult()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if have, want := string(res), `"0,0"`; have != want {
+ t.Errorf("wrong result for tracer, have \n%v\nwant\n%v\n", have, want)
+ }
+}
+
+func BenchmarkTracerStepVsCallFrame(b *testing.B) {
+ // Simply pushes and pops some values in a loop
+ code := []byte{
+ byte(vm.JUMPDEST),
+ byte(vm.PUSH1), 0,
+ byte(vm.PUSH1), 0,
+ byte(vm.POP),
+ byte(vm.POP),
+ byte(vm.PUSH1), 0, // jumpdestination
+ byte(vm.JUMP),
+ }
+
+ stepTracer := `
+ {
+ step: function() {},
+ fault: function() {},
+ result: function() {},
+ }`
+ callFrameTracer := `
+ {
+ enter: function() {},
+ exit: function() {},
+ fault: function() {},
+ result: function() {},
+ }`
+
+ benchmarkNonModifyingCode(10000000, code, "tracer-step-10M", stepTracer, b)
+ benchmarkNonModifyingCode(10000000, code, "tracer-call-frame-10M", callFrameTracer, b)
+}
diff --git a/core/vm/stack.go b/core/vm/stack.go
index c71d2653a5..220f97c89f 100644
--- a/core/vm/stack.go
+++ b/core/vm/stack.go
@@ -91,7 +91,7 @@ func (st *Stack) Print() {
fmt.Println("### stack ###")
if len(st.data) > 0 {
for i, val := range st.data {
- fmt.Printf("%-3d %v\n", i, val)
+ fmt.Printf("%-3d %s\n", i, val.String())
}
} else {
fmt.Println("-- empty --")
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.go
index ab73342803..b2f4057074 100644
--- a/crypto/blake2b/blake2b_f_fuzz.go
+++ b/crypto/blake2b/blake2b_f_fuzz.go
@@ -1,3 +1,4 @@
+//go:build gofuzz
// +build gofuzz
package blake2b
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/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/bls12381/arithmetic_decl.go b/crypto/bls12381/arithmetic_decl.go
index ec0b21e805..f6d232d658 100644
--- a/crypto/bls12381/arithmetic_decl.go
+++ b/crypto/bls12381/arithmetic_decl.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 (amd64 && blsasm) || (amd64 && blsadx)
// +build amd64,blsasm amd64,blsadx
package bls12381
diff --git a/crypto/bls12381/arithmetic_fallback.go b/crypto/bls12381/arithmetic_fallback.go
index 91cabf4f3d..c09ae0d91c 100644
--- a/crypto/bls12381/arithmetic_fallback.go
+++ b/crypto/bls12381/arithmetic_fallback.go
@@ -31,6 +31,7 @@
// Package bls (generated by goff) contains field arithmetics operations
+//go:build !amd64 || (!blsasm && !blsadx)
// +build !amd64 !blsasm,!blsadx
package bls12381
diff --git a/crypto/bls12381/arithmetic_x86_adx.go b/crypto/bls12381/arithmetic_x86_adx.go
index 9c30741e6a..a40c7384eb 100644
--- a/crypto/bls12381/arithmetic_x86_adx.go
+++ b/crypto/bls12381/arithmetic_x86_adx.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 amd64 && blsadx
// +build amd64,blsadx
package bls12381
diff --git a/crypto/bls12381/arithmetic_x86_noadx.go b/crypto/bls12381/arithmetic_x86_noadx.go
index eaac4b45d7..679b30ec8c 100644
--- a/crypto/bls12381/arithmetic_x86_noadx.go
+++ b/crypto/bls12381/arithmetic_x86_noadx.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 amd64 && blsasm
// +build amd64,blsasm
package bls12381
diff --git a/crypto/bn256/bn256_fast.go b/crypto/bn256/bn256_fast.go
index 14b5965393..e3c9b60518 100644
--- a/crypto/bn256/bn256_fast.go
+++ b/crypto/bn256/bn256_fast.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 || arm64
// +build amd64 arm64
// Package bn256 implements the Optimal Ate pairing over a 256-bit Barreto-Naehrig curve.
diff --git a/crypto/bn256/bn256_slow.go b/crypto/bn256/bn256_slow.go
index 49021082f5..4c0c351e2d 100644
--- a/crypto/bn256/bn256_slow.go
+++ b/crypto/bn256/bn256_slow.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 && !arm64
// +build !amd64,!arm64
// Package bn256 implements the Optimal Ate pairing over a 256-bit Barreto-Naehrig curve.
diff --git a/crypto/bn256/cloudflare/bn256_test.go b/crypto/bn256/cloudflare/bn256_test.go
index 0c8016d86c..481e2f78c3 100644
--- a/crypto/bn256/cloudflare/bn256_test.go
+++ b/crypto/bn256/cloudflare/bn256_test.go
@@ -92,6 +92,19 @@ func TestTripartiteDiffieHellman(t *testing.T) {
}
}
+func TestG2SelfAddition(t *testing.T) {
+ s, _ := rand.Int(rand.Reader, Order)
+ p := new(G2).ScalarBaseMult(s)
+
+ if !p.p.IsOnCurve() {
+ t.Fatal("p isn't on curve")
+ }
+ m := p.Add(p, p).Marshal()
+ if _, err := p.Unmarshal(m); err != nil {
+ t.Fatalf("p.Add(p, p) ∉ G₂: %v", err)
+ }
+}
+
func BenchmarkG1(b *testing.B) {
x, _ := rand.Int(rand.Reader, Order)
b.ResetTimer()
diff --git a/crypto/bn256/cloudflare/curve.go b/crypto/bn256/cloudflare/curve.go
index 18e9b38f3f..16f0489e33 100644
--- a/crypto/bn256/cloudflare/curve.go
+++ b/crypto/bn256/cloudflare/curve.go
@@ -171,15 +171,15 @@ func (c *curvePoint) Double(a *curvePoint) {
gfpAdd(t, d, d)
gfpSub(&c.x, f, t)
+ gfpMul(&c.z, &a.y, &a.z)
+ gfpAdd(&c.z, &c.z, &c.z)
+
gfpAdd(t, C, C)
gfpAdd(t2, t, t)
gfpAdd(t, t2, t2)
gfpSub(&c.y, d, &c.x)
gfpMul(t2, e, &c.y)
gfpSub(&c.y, t2, t)
-
- gfpMul(t, &a.y, &a.z)
- gfpAdd(&c.z, t, t)
}
func (c *curvePoint) Mul(a *curvePoint, scalar *big.Int) {
diff --git a/crypto/bn256/cloudflare/gfp.go b/crypto/bn256/cloudflare/gfp.go
index e8e84e7b3b..b15e1697e1 100644
--- a/crypto/bn256/cloudflare/gfp.go
+++ b/crypto/bn256/cloudflare/gfp.go
@@ -61,6 +61,7 @@ func (e *gfP) Marshal(out []byte) {
func (e *gfP) Unmarshal(in []byte) error {
// Unmarshal the bytes into little endian form
for w := uint(0); w < 4; w++ {
+ e[3-w] = 0
for b := uint(0); b < 8; b++ {
e[3-w] += uint64(in[8*w+b]) << (56 - 8*b)
}
diff --git a/crypto/bn256/cloudflare/gfp_decl.go b/crypto/bn256/cloudflare/gfp_decl.go
index fdea5c11a5..ec4018e88a 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
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/twist.go b/crypto/bn256/cloudflare/twist.go
index 0c2f80d4ed..2c7a69a4d7 100644
--- a/crypto/bn256/cloudflare/twist.go
+++ b/crypto/bn256/cloudflare/twist.go
@@ -150,15 +150,15 @@ func (c *twistPoint) Double(a *twistPoint) {
t.Add(d, d)
c.x.Sub(f, t)
+ c.z.Mul(&a.y, &a.z)
+ c.z.Add(&c.z, &c.z)
+
t.Add(C, C)
t2.Add(t, t)
t.Add(t2, t2)
c.y.Sub(d, &c.x)
t2.Mul(e, &c.y)
c.y.Sub(t2, t)
-
- t.Mul(&a.y, &a.z)
- c.z.Add(t, t)
}
func (c *twistPoint) Mul(a *twistPoint, scalar *big.Int) {
diff --git a/crypto/secp256k1/dummy.go b/crypto/secp256k1/dummy.go
index c0f2ee52c0..65a75080f6 100644
--- a/crypto/secp256k1/dummy.go
+++ b/crypto/secp256k1/dummy.go
@@ -1,3 +1,4 @@
+//go:build dummy
// +build dummy
// This file is part of a workaround for `go mod vendor` which won't vendor
diff --git a/crypto/secp256k1/panic_cb.go b/crypto/secp256k1/panic_cb.go
index 5da2bea376..a30b04f51b 100644
--- a/crypto/secp256k1/panic_cb.go
+++ b/crypto/secp256k1/panic_cb.go
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
-// +build !gofuzz
-// +build cgo
+//go:build !gofuzz && cgo
+// +build !gofuzz,cgo
package secp256k1
diff --git a/crypto/secp256k1/scalar_mult_cgo.go b/crypto/secp256k1/scalar_mult_cgo.go
index f28a1c7826..8afa9d023b 100644
--- a/crypto/secp256k1/scalar_mult_cgo.go
+++ b/crypto/secp256k1/scalar_mult_cgo.go
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
-// +build !gofuzz
-// +build cgo
+//go:build !gofuzz && cgo
+// +build !gofuzz,cgo
package secp256k1
diff --git a/crypto/secp256k1/scalar_mult_nocgo.go b/crypto/secp256k1/scalar_mult_nocgo.go
index 55756b5be8..22f53ac6ae 100644
--- a/crypto/secp256k1/scalar_mult_nocgo.go
+++ b/crypto/secp256k1/scalar_mult_nocgo.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 gofuzz || !cgo
// +build gofuzz !cgo
package secp256k1
diff --git a/crypto/secp256k1/secp256.go b/crypto/secp256k1/secp256.go
index a1bcf7796e..c9c01b3209 100644
--- a/crypto/secp256k1/secp256.go
+++ b/crypto/secp256k1/secp256.go
@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be found in
// the LICENSE file.
-// +build !gofuzz
-// +build cgo
+//go:build !gofuzz && cgo
+// +build !gofuzz,cgo
// Package secp256k1 wraps the bitcoin secp256k1 C library.
package secp256k1
diff --git a/crypto/signature_cgo.go b/crypto/signature_cgo.go
index 8433602980..bd72d97d3b 100644
--- a/crypto/signature_cgo.go
+++ b/crypto/signature_cgo.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 !nacl && !js && cgo && !gofuzz
// +build !nacl,!js,cgo,!gofuzz
package crypto
diff --git a/crypto/signature_nocgo.go b/crypto/signature_nocgo.go
index 77c8a1db0b..fd1e66c7e6 100644
--- a/crypto/signature_nocgo.go
+++ b/crypto/signature_nocgo.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 nacl || js || !cgo || gofuzz
// +build nacl js !cgo gofuzz
package crypto
diff --git a/crypto/signify/signify_fuzz.go b/crypto/signify/signify_fuzz.go
index f9167900ad..2dc9b2102f 100644
--- a/crypto/signify/signify_fuzz.go
+++ b/crypto/signify/signify_fuzz.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 gofuzz
// +build gofuzz
package signify
diff --git a/docs/postmortems/2021-08-22-split-postmortem.md b/docs/postmortems/2021-08-22-split-postmortem.md
new file mode 100644
index 0000000000..429f22d70a
--- /dev/null
+++ b/docs/postmortems/2021-08-22-split-postmortem.md
@@ -0,0 +1,266 @@
+# Minority split 2021-08-27 post mortem
+
+This is a post-mortem concerning the minority split that occurred on Ethereum mainnet on block [13107518](https://etherscan.io/block/13107518), at which a minority chain split occurred.
+
+## Timeline
+
+
+- 2021-08-17: Guido Vranken submitted bounty report. Investigation started, root cause identified, patch variations discussed.
+- 2021-08-18: Made public announcement over twitter about upcoming security release upcoming Tuesday. Downstream projects were also notified about the upcoming patch-release.
+- 2021-08-24: Released [v1.10.8](https://github.com/ethereum/go-ethereum/releases/tag/v1.10.8) containing the fix on Tuesday morning (CET). Erigon released [v2021.08.04](https://github.com/ledgerwatch/erigon/releases/tag/v2021.08.04).
+- 2021-08-27: At 12:50:07 UTC, issue exploited. Analysis started roughly 30m later,
+
+
+
+## Bounty report
+
+### 2021-08-17 RETURNDATA corruption via datacopy
+
+On 2021-08-17, Guido Vranken submitted a report to bounty@ethereum.org. This coincided with a geth-meetup in Berlin, so the geth team could fairly quickly analyse the issue.
+
+He submitted a proof of concept which called the `dataCopy` precompile, where the input slice and output slice were overlapping but shifted. Doing a `copy` where the `src` and `dest` overlaps is not a problem in itself, however, the `returnData`slice was _also_ using the same memory as a backing-array.
+
+#### Technical details
+
+During CALL-variants, `geth` does not copy the input. This was changed at one point, to avoid a DoS attack reported by Hubert Ritzdorf, to avoid copying data a lot on repeated `CALL`s -- essentially combating a DoS via `malloc`. Further, the datacopy precompile also does not copy the data, but just returns the same slice. This is fine so far.
+
+After the execution of `dataCopy`, we copy the `ret` into the designated memory area, and this is what causes a problem. Because we're copying a slice of memory over a slice of memory, and this operation modifies (shifts) the data in the source -- the `ret`. So this means we wind up with corrupted returndata.
+
+
+```
+1. Calling datacopy
+
+ memory: [0, 1, 2, 3, 4]
+ in (mem[0:4]) : [0,1,2,3]
+ out (mem[1:5]): [1,2,3,4]
+
+2. dataCopy returns
+
+ returndata (==in, mem[0:4]): [0,1,2,3]
+
+3. Copy in -> out
+
+ => memory: [0,0,1,2,3]
+ => returndata: [0,0,1,2]
+```
+
+
+#### Summary
+
+A memory-corruption bug within the EVM can cause a consensus error, where vulnerable nodes obtain a different `stateRoot` when processing a maliciously crafted transaction. This, in turn, would lead to the chain being split: mainnet splitting in two forks.
+
+#### Handling
+
+On the evening of 17th, we discussed options how to handle it. We made a state test to reproduce the issue, and verified that neither `openethereum`, `nethermind` nor `besu` were affected by the same vulnerability, and started a full-sync with a patched version of `geth`.
+
+It was decided that in this specific instance, it would be possible to make a public announcement and a patch release:
+
+- The fix can be made pretty 'generically', e.g. always copying data on input to precompiles.
+- The flaw is pretty difficult to find, given a generic fix in the call. The attacker needs to figure out that it concerns the precompiles, specifically the datcopy, and that it concerns the `RETURNDATA` buffer rather than the regular memory, and lastly the special circumstances to trigger it (overlapping but shifted input/output).
+
+Since we had merged the removal of `ETH65`, if the entire network were to upgrade, then nodes which have not yet implemented `ETH66` would be cut off from the network. After further discussions, we decided to:
+
+- Announce an upcoming security release on Tuesday (August 24th), via Twitter and official channels, plus reach out to downstream projects.
+- Temporarily revert the `ETH65`-removal.
+- Place the fix into the PR optimizing the jumpdest analysis [233381](https://github.com/ethereum/go-ethereum/pull/23381).
+- After 4-8 weeks, release details about the vulnerability.
+
+
+## Exploit
+
+At block [13107518](https://etherscan.io/block/13107518), mined at (Aug-27-2021 12:50:07 PM +UTC), a minority chain split occurred. The discord user @AlexSSD7 notified the allcoredevs-channel on the Eth R&D discord, on Aug 27 13:09 UTC.
+
+
+At 14:09 UTC, it was confirmed that the transaction `0x1cb6fb36633d270edefc04d048145b4298e67b8aa82a9e5ec4aa1435dd770ce4` had triggered the bug, leading to a minority-split of the chain. The term 'minority split' means that the majority of miners continued to mine on the correct chain.
+
+At 14:17 UTC, @mhswende tweeted out about the issue [2].
+
+The attack was sent from an account funded from Tornado cash.
+
+It was also found that the same attack had been carried out on the BSC chain at roughly the same time -- at a block mined [12 minutes earlier](https://bscscan.com/tx/0xf667f820631f6adbd04a4c92274374034a3e41fa9057dc42cb4e787535136dce), at Aug-27-2021 12:38:30 PM +UTC.
+
+The blocks on the 'bad' chain were investigated, and Tim Beiko reached out to those mining operators on the minority chain who could be identified via block extradata.
+
+
+## Lessons learned
+
+
+### Disclosure decision
+
+The geth-team have an official policy regarding [vulnerability disclosure](https://geth.ethereum.org/docs/vulnerabilities/vulnerabilities).
+
+> The primary goal for the Geth team is the health of the Ethereum network as a whole, and the decision whether or not to publish details about a serious vulnerability boils down to minimizing the risk and/or impact of discovery and exploitation.
+
+In this case, it was decided that public pre-announce + patch would likely lead to sufficient update-window for a critical mass of nodes/miners to upgrade in time before it could be exploited. In hindsight, this was a dangerous decision, and it's unlikely that the same decision would be reached were a similar incident to happen again.
+
+
+### Disclosure path
+
+Several subprojects were informed about the upcoming security patch:
+
+- Polygon/Matic
+- MEV
+- Avalanche
+- Erigon
+- BSC
+- EWF
+- Quorum
+- ETC
+- xDAI
+
+However, some were 'lost', and only notified later
+
+- Optimism
+- Summa
+- Harmony
+
+Action point: create a low-volume geth-announce@ethereum.org email list where dependent projects/operators can receive public announcements.
+- This has been done. If you wish to receive release- and security announcements, sign up [here](https://groups.google.com/a/ethereum.org/g/geth-announce/about)
+
+### Fork monitoring
+
+The fork monitor behaved 'ok' during the incident, but had to be restarted during the evening.
+
+Action point: improve the resiliency of the forkmon, which is currently not performing great when many nodes are connected.
+
+Action point: enable push-based alerts to be sent from the forkmon, to speed up the fork detection.
+
+
+## Links
+
+- [1] https://twitter.com/go_ethereum/status/1428051458763763721
+- [2] https://twitter.com/mhswende/status/1431259601530458112
+
+
+## Appendix
+
+### Subprojects
+
+
+The projects were sent variations of the following text:
+```
+We have identified a security issue with go-ethereum, and will issue a
+new release (v1.10.8) on Tuesday next week.
+
+At this point, we will not disclose details about the issue, but
+recommend downstream/dependent projects to be ready to take actions to
+upgrade to the latest go-ethereum codebase. More information about the
+issue will be disclosed at a later date.
+
+https://twitter.com/go_ethereum/status/1428051458763763721
+
+```
+### Patch
+
+```diff
+diff --git a/core/vm/instructions.go b/core/vm/instructions.go
+index f7ef2f900e..6c8c6e6e6f 100644
+--- a/core/vm/instructions.go
++++ b/core/vm/instructions.go
+@@ -669,6 +669,7 @@ 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
+@@ -703,6 +704,7 @@ 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
+@@ -730,6 +732,7 @@ 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
+@@ -757,6 +760,7 @@ 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
+diff --git a/core/vm/interpreter.go b/core/vm/interpreter.go
+index 9cf0c4e2c1..9fb83799c9 100644
+--- a/core/vm/interpreter.go
++++ b/core/vm/interpreter.go
+@@ -262,7 +262,7 @@ func (in *EVMInterpreter) Run(contract *Contract, input []byte, readOnly bool) (
+ // if the operation clears the return data (e.g. it has returning data)
+ // set the last return to the result of the operation.
+ if operation.returns {
+- in.returnData = common.CopyBytes(res)
++ in.returnData = res
+ }
+
+ switch {
+```
+
+### Statetest to test for the issue
+
+```json
+{
+ "trigger-issue": {
+ "env": {
+ "currentCoinbase": "b94f5374fce5edbc8e2a8697c15331677e6ebf0b",
+ "currentDifficulty": "0x20000",
+ "currentGasLimit": "0x26e1f476fe1e22",
+ "currentNumber": "0x1",
+ "currentTimestamp": "0x3e8",
+ "previousHash": "0x0000000000000000000000000000000000000000000000000000000000000000"
+ },
+ "pre": {
+ "0x00000000000000000000000000000000000000bb": {
+ "code": "0x6001600053600260015360036002536004600353600560045360066005536006600260066000600060047f7ef0367e633852132a0ebbf70eb714015dd44bc82e1e55a96ef1389c999c1bcaf13d600060003e596000208055",
+ "storage": {},
+ "balance": "0x5",
+ "nonce": "0x0"
+ },
+ "0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b": {
+ "code": "0x",
+ "storage": {},
+ "balance": "0xffffffff",
+ "nonce": "0x0"
+ }
+ },
+ "transaction": {
+ "gasPrice": "0x1",
+ "nonce": "0x0",
+ "to": "0x00000000000000000000000000000000000000bb",
+ "data": [
+ "0x"
+ ],
+ "gasLimit": [
+ "0x7a1200"
+ ],
+ "value": [
+ "0x01"
+ ],
+ "secretKey": "0x45a915e4d060149eb4365960e6a7a45f334393093061116b197e3240065ff2d8"
+ },
+ "out": "0x",
+ "post": {
+ "Berlin": [
+ {
+ "hash": "2a38a040bab1e1fa499253d98b2fd363e5756ecc52db47dd59af7116c068368c",
+ "logs": "1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
+ "indexes": {
+ "data": 0,
+ "gas": 0,
+ "value": 0
+ }
+ }
+ ]
+ }
+ }
+}
+```
+
diff --git a/eth/api.go b/eth/api.go
index 0f57128d75..3ec72c58e4 100644
--- a/eth/api.go
+++ b/eth/api.go
@@ -35,6 +35,7 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/internal/ethapi"
+ "github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/rpc"
"github.com/ethereum/go-ethereum/trie"
@@ -342,7 +343,7 @@ func (api *PrivateDebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs,
} else {
blockRlp = fmt.Sprintf("0x%x", rlpBytes)
}
- if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true, api.eth.engine); err != nil {
+ if blockJSON, err = ethapi.RPCMarshalBlock(block, true, true, api.eth.APIBackend.ChainConfig()); err != nil {
blockJSON = map[string]interface{}{"error": err.Error()}
}
results = append(results, &BadBlockArgs{
@@ -545,3 +546,64 @@ func (api *PrivateDebugAPI) getModifiedAccounts(startBlock, endBlock *types.Bloc
}
return dirty, nil
}
+
+// GetAccessibleState returns the first number where the node has accessible
+// state on disk. Note this being the post-state of that block and the pre-state
+// of the next block.
+// The (from, to) parameters are the sequence of blocks to search, which can go
+// either forwards or backwards
+func (api *PrivateDebugAPI) GetAccessibleState(from, to rpc.BlockNumber) (uint64, error) {
+ db := api.eth.ChainDb()
+ var pivot uint64
+ if p := rawdb.ReadLastPivotNumber(db); p != nil {
+ pivot = *p
+ log.Info("Found fast-sync pivot marker", "number", pivot)
+ }
+ var resolveNum = func(num rpc.BlockNumber) (uint64, error) {
+ // We don't have state for pending (-2), so treat it as latest
+ if num.Int64() < 0 {
+ block := api.eth.blockchain.CurrentBlock()
+ if block == nil {
+ return 0, fmt.Errorf("current block missing")
+ }
+ return block.NumberU64(), nil
+ }
+ return uint64(num.Int64()), nil
+ }
+ var (
+ start uint64
+ end uint64
+ delta = int64(1)
+ lastLog time.Time
+ err error
+ )
+ if start, err = resolveNum(from); err != nil {
+ return 0, err
+ }
+ if end, err = resolveNum(to); err != nil {
+ return 0, err
+ }
+ if start == end {
+ return 0, fmt.Errorf("from and to needs to be different")
+ }
+ if start > end {
+ delta = -1
+ }
+ for i := int64(start); i != int64(end); i += delta {
+ if time.Since(lastLog) > 8*time.Second {
+ log.Info("Finding roots", "from", start, "to", end, "at", i)
+ lastLog = time.Now()
+ }
+ if i < int64(pivot) {
+ continue
+ }
+ h := api.eth.BlockChain().GetHeaderByNumber(uint64(i))
+ if h == nil {
+ return 0, fmt.Errorf("missing header %d", i)
+ }
+ if ok, _ := api.eth.ChainDb().Has(h.Root[:]); ok {
+ return uint64(i), nil
+ }
+ }
+ return 0, fmt.Errorf("No state found")
+}
diff --git a/eth/api_backend.go b/eth/api_backend.go
index 49de70e210..01e68f6781 100644
--- a/eth/api_backend.go
+++ b/eth/api_backend.go
@@ -20,7 +20,9 @@ import (
"context"
"errors"
"math/big"
+ "time"
+ "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
@@ -30,7 +32,6 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
@@ -181,19 +182,23 @@ func (b *EthAPIBackend) GetReceipts(ctx context.Context, hash common.Hash) (type
}
func (b *EthAPIBackend) GetLogs(ctx context.Context, hash common.Hash) ([][]*types.Log, error) {
- receipts := b.eth.blockchain.GetReceiptsByHash(hash)
- if receipts == nil {
- return nil, nil
+ db := b.eth.ChainDb()
+ number := rawdb.ReadHeaderNumber(db, hash)
+ if number == nil {
+ return nil, errors.New("failed to get block number from hash")
}
- logs := make([][]*types.Log, len(receipts))
- for i, receipt := range receipts {
- logs[i] = receipt.Logs
+ logs := rawdb.ReadLogs(db, hash, *number)
+ if logs == nil {
+ return nil, errors.New("failed to get logs for block")
}
return logs, nil
}
func (b *EthAPIBackend) GetTd(ctx context.Context, hash common.Hash) *big.Int {
- return b.eth.blockchain.GetTdByHash(hash)
+ if header := b.eth.blockchain.GetHeaderByHash(hash); header != nil {
+ return b.eth.blockchain.GetTd(hash, header.Number.Uint64())
+ }
+ return nil
}
func (b *EthAPIBackend) GetEVM(ctx context.Context, msg core.Message, state *state.StateDB, header *types.Header, vmConfig *vm.Config) (*vm.EVM, func() error, error) {
@@ -235,10 +240,7 @@ func (b *EthAPIBackend) SendTx(ctx context.Context, signedTx *types.Transaction)
}
func (b *EthAPIBackend) GetPoolTransactions() (types.Transactions, error) {
- pending, err := b.eth.txPool.Pending(false)
- if err != nil {
- return nil, err
- }
+ pending := b.eth.txPool.Pending(false)
var txs types.Transactions
for _, batch := range pending {
txs = append(txs, batch...)
@@ -279,8 +281,8 @@ func (b *EthAPIBackend) SubscribeNewTxsEvent(ch chan<- core.NewTxsEvent) event.S
return b.eth.TxPool().SubscribeNewTxsEvent(ch)
}
-func (b *EthAPIBackend) Downloader() *downloader.Downloader {
- return b.eth.Downloader()
+func (b *EthAPIBackend) SyncProgress() ethereum.SyncProgress {
+ return b.eth.Downloader().Progress()
}
func (b *EthAPIBackend) SuggestGasTipCap(ctx context.Context) (*big.Int, error) {
@@ -315,6 +317,10 @@ func (b *EthAPIBackend) RPCGasCap() uint64 {
return b.eth.config.RPCGasCap
}
+func (b *EthAPIBackend) RPCEVMTimeout() time.Duration {
+ return b.eth.config.RPCEVMTimeout
+}
+
func (b *EthAPIBackend) RPCTxFeeCap() float64 {
return b.eth.config.RPCTxFeeCap
}
diff --git a/eth/backend.go b/eth/backend.go
index 793d3b81f1..ae4e6e85d7 100644
--- a/eth/backend.go
+++ b/eth/backend.go
@@ -131,7 +131,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*Ethereum, error) {
if err != nil {
return nil, err
}
- chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideLondon)
+ chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideArrowGlacier)
if _, ok := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !ok {
return nil, genesisErr
}
@@ -554,7 +554,7 @@ func (s *Ethereum) Stop() error {
s.bloomIndexer.Close()
close(s.closeBloomHandler)
s.txPool.Stop()
- s.miner.Stop()
+ s.miner.Close()
s.blockchain.Stop()
s.engine.Close()
rawdb.PopUncleanShutdownMarker(s.chainDb)
diff --git a/eth/catalyst/api.go b/eth/catalyst/api.go
index 2622c4a148..3913da7572 100644
--- a/eth/catalyst/api.go
+++ b/eth/catalyst/api.go
@@ -39,10 +39,8 @@ import (
// Register adds catalyst APIs to the node.
func Register(stack *node.Node, backend *eth.Ethereum) error {
chainconfig := backend.BlockChain().Config()
- if chainconfig.CatalystBlock == nil {
- return errors.New("catalystBlock is not set in genesis config")
- } else if chainconfig.CatalystBlock.Sign() != 0 {
- return errors.New("catalystBlock of genesis config must be zero")
+ if chainconfig.TerminalTotalDifficulty == nil {
+ return errors.New("catalyst started without valid total difficulty")
}
log.Warn("Catalyst mode enabled")
@@ -128,10 +126,7 @@ func (api *consensusAPI) AssembleBlock(params assembleBlockParams) (*executableD
time.Sleep(wait)
}
- pending, err := pool.Pending(true)
- if err != nil {
- return nil, err
- }
+ pending := pool.Pending(true)
coinbase, err := api.eth.Etherbase()
if err != nil {
diff --git a/eth/catalyst/api_test.go b/eth/catalyst/api_test.go
index f8550fa59b..1104208630 100644
--- a/eth/catalyst/api_test.go
+++ b/eth/catalyst/api_test.go
@@ -62,26 +62,28 @@ func generateTestChain() (*core.Genesis, []*types.Block) {
return genesis, blocks
}
+// TODO (MariusVanDerWijden) reenable once engine api is updated to the latest spec
+/*
func generateTestChainWithFork(n int, fork int) (*core.Genesis, []*types.Block, []*types.Block) {
if fork >= n {
fork = n - 1
}
db := rawdb.NewMemoryDatabase()
config := ¶ms.ChainConfig{
- ChainID: big.NewInt(1337),
- 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),
- MuirGlacierBlock: big.NewInt(0),
- BerlinBlock: big.NewInt(0),
- LondonBlock: big.NewInt(0),
- CatalystBlock: big.NewInt(0),
- Ethash: new(params.EthashConfig),
+ ChainID: big.NewInt(1337),
+ 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),
+ MuirGlacierBlock: big.NewInt(0),
+ BerlinBlock: big.NewInt(0),
+ LondonBlock: big.NewInt(0),
+ TerminalTotalDifficulty: big.NewInt(0),
+ Ethash: new(params.EthashConfig),
}
genesis := &core.Genesis{
Config: config,
@@ -105,6 +107,7 @@ func generateTestChainWithFork(n int, fork int) (*core.Genesis, []*types.Block,
forkedBlocks, _ := core.GenerateChain(config, blocks[fork], engine, db, n-fork, generateFork)
return genesis, blocks, forkedBlocks
}
+*/
func TestEth2AssembleBlock(t *testing.T) {
genesis, blocks := generateTestChain()
@@ -156,6 +159,8 @@ func TestEth2AssembleBlockWithAnotherBlocksTxs(t *testing.T) {
}
}
+// TODO (MariusVanDerWijden) reenable once engine api is updated to the latest spec
+/*
func TestEth2NewBlock(t *testing.T) {
genesis, blocks, forkedBlocks := generateTestChainWithFork(10, 4)
n, ethservice := startEthService(t, genesis, blocks[1:5])
@@ -216,6 +221,7 @@ func TestEth2NewBlock(t *testing.T) {
t.Fatalf("Wrong head after inserting fork %x != %x", exp, ethservice.BlockChain().CurrentBlock().Hash())
}
}
+*/
// startEthService creates a full node instance for testing.
func startEthService(t *testing.T, genesis *core.Genesis, blocks []*types.Block) (*node.Node, *eth.Ethereum) {
diff --git a/eth/downloader/downloader.go b/eth/downloader/downloader.go
index 1767506a33..a6bf87acbd 100644
--- a/eth/downloader/downloader.go
+++ b/eth/downloader/downloader.go
@@ -448,8 +448,8 @@ func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.I
d.mux.Post(DoneEvent{latest})
}
}()
- if p.version < eth.ETH65 {
- return fmt.Errorf("%w: advertized %d < required %d", errTooOld, p.version, eth.ETH65)
+ if p.version < eth.ETH66 {
+ return fmt.Errorf("%w: advertized %d < required %d", errTooOld, p.version, eth.ETH66)
}
mode := d.getMode()
diff --git a/eth/downloader/downloader_test.go b/eth/downloader/downloader_test.go
index 794160993b..17cd3630c9 100644
--- a/eth/downloader/downloader_test.go
+++ b/eth/downloader/downloader_test.go
@@ -522,10 +522,6 @@ func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, leng
}
}
-func TestCanonicalSynchronisation65Full(t *testing.T) { testCanonSync(t, eth.ETH65, FullSync) }
-func TestCanonicalSynchronisation65Fast(t *testing.T) { testCanonSync(t, eth.ETH65, FastSync) }
-func TestCanonicalSynchronisation65Light(t *testing.T) { testCanonSync(t, eth.ETH65, LightSync) }
-
func TestCanonicalSynchronisation66Full(t *testing.T) { testCanonSync(t, eth.ETH66, FullSync) }
func TestCanonicalSynchronisation66Fast(t *testing.T) { testCanonSync(t, eth.ETH66, FastSync) }
func TestCanonicalSynchronisation66Light(t *testing.T) { testCanonSync(t, eth.ETH66, LightSync) }
@@ -549,9 +545,6 @@ func testCanonSync(t *testing.T, protocol uint, mode SyncMode) {
// Tests that if a large batch of blocks are being downloaded, it is throttled
// until the cached blocks are retrieved.
-func TestThrottling65Full(t *testing.T) { testThrottling(t, eth.ETH65, FullSync) }
-func TestThrottling65Fast(t *testing.T) { testThrottling(t, eth.ETH65, FastSync) }
-
func TestThrottling66Full(t *testing.T) { testThrottling(t, eth.ETH66, FullSync) }
func TestThrottling66Fast(t *testing.T) { testThrottling(t, eth.ETH66, FastSync) }
@@ -634,10 +627,6 @@ func testThrottling(t *testing.T, protocol uint, mode SyncMode) {
// Tests that simple synchronization against a forked chain works correctly. In
// this test common ancestor lookup should *not* be short circuited, and a full
// binary search should be executed.
-func TestForkedSync65Full(t *testing.T) { testForkedSync(t, eth.ETH65, FullSync) }
-func TestForkedSync65Fast(t *testing.T) { testForkedSync(t, eth.ETH65, FastSync) }
-func TestForkedSync65Light(t *testing.T) { testForkedSync(t, eth.ETH65, LightSync) }
-
func TestForkedSync66Full(t *testing.T) { testForkedSync(t, eth.ETH66, FullSync) }
func TestForkedSync66Fast(t *testing.T) { testForkedSync(t, eth.ETH66, FastSync) }
func TestForkedSync66Light(t *testing.T) { testForkedSync(t, eth.ETH66, LightSync) }
@@ -667,10 +656,6 @@ func testForkedSync(t *testing.T, protocol uint, mode SyncMode) {
// Tests that synchronising against a much shorter but much heavyer fork works
// corrently and is not dropped.
-func TestHeavyForkedSync65Full(t *testing.T) { testHeavyForkedSync(t, eth.ETH65, FullSync) }
-func TestHeavyForkedSync65Fast(t *testing.T) { testHeavyForkedSync(t, eth.ETH65, FastSync) }
-func TestHeavyForkedSync65Light(t *testing.T) { testHeavyForkedSync(t, eth.ETH65, LightSync) }
-
func TestHeavyForkedSync66Full(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, FullSync) }
func TestHeavyForkedSync66Fast(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, FastSync) }
func TestHeavyForkedSync66Light(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, LightSync) }
@@ -702,10 +687,6 @@ func testHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) {
// Tests that chain forks are contained within a certain interval of the current
// chain head, ensuring that malicious peers cannot waste resources by feeding
// long dead chains.
-func TestBoundedForkedSync65Full(t *testing.T) { testBoundedForkedSync(t, eth.ETH65, FullSync) }
-func TestBoundedForkedSync65Fast(t *testing.T) { testBoundedForkedSync(t, eth.ETH65, FastSync) }
-func TestBoundedForkedSync65Light(t *testing.T) { testBoundedForkedSync(t, eth.ETH65, LightSync) }
-
func TestBoundedForkedSync66Full(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, FullSync) }
func TestBoundedForkedSync66Fast(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, FastSync) }
func TestBoundedForkedSync66Light(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, LightSync) }
@@ -736,16 +717,6 @@ func testBoundedForkedSync(t *testing.T, protocol uint, mode SyncMode) {
// Tests that chain forks are contained within a certain interval of the current
// chain head for short but heavy forks too. These are a bit special because they
// take different ancestor lookup paths.
-func TestBoundedHeavyForkedSync65Full(t *testing.T) {
- testBoundedHeavyForkedSync(t, eth.ETH65, FullSync)
-}
-func TestBoundedHeavyForkedSync65Fast(t *testing.T) {
- testBoundedHeavyForkedSync(t, eth.ETH65, FastSync)
-}
-func TestBoundedHeavyForkedSync65Light(t *testing.T) {
- testBoundedHeavyForkedSync(t, eth.ETH65, LightSync)
-}
-
func TestBoundedHeavyForkedSync66Full(t *testing.T) {
testBoundedHeavyForkedSync(t, eth.ETH66, FullSync)
}
@@ -800,10 +771,6 @@ func TestInactiveDownloader63(t *testing.T) {
}
// Tests that a canceled download wipes all previously accumulated state.
-func TestCancel65Full(t *testing.T) { testCancel(t, eth.ETH65, FullSync) }
-func TestCancel65Fast(t *testing.T) { testCancel(t, eth.ETH65, FastSync) }
-func TestCancel65Light(t *testing.T) { testCancel(t, eth.ETH65, LightSync) }
-
func TestCancel66Full(t *testing.T) { testCancel(t, eth.ETH66, FullSync) }
func TestCancel66Fast(t *testing.T) { testCancel(t, eth.ETH66, FastSync) }
func TestCancel66Light(t *testing.T) { testCancel(t, eth.ETH66, LightSync) }
@@ -833,10 +800,6 @@ func testCancel(t *testing.T, protocol uint, mode SyncMode) {
}
// Tests that synchronisation from multiple peers works as intended (multi thread sanity test).
-func TestMultiSynchronisation65Full(t *testing.T) { testMultiSynchronisation(t, eth.ETH65, FullSync) }
-func TestMultiSynchronisation65Fast(t *testing.T) { testMultiSynchronisation(t, eth.ETH65, FastSync) }
-func TestMultiSynchronisation65Light(t *testing.T) { testMultiSynchronisation(t, eth.ETH65, LightSync) }
-
func TestMultiSynchronisation66Full(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, FullSync) }
func TestMultiSynchronisation66Fast(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, FastSync) }
func TestMultiSynchronisation66Light(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, LightSync) }
@@ -863,10 +826,6 @@ func testMultiSynchronisation(t *testing.T, protocol uint, mode SyncMode) {
// Tests that synchronisations behave well in multi-version protocol environments
// and not wreak havoc on other nodes in the network.
-func TestMultiProtoSynchronisation65Full(t *testing.T) { testMultiProtoSync(t, eth.ETH65, FullSync) }
-func TestMultiProtoSynchronisation65Fast(t *testing.T) { testMultiProtoSync(t, eth.ETH65, FastSync) }
-func TestMultiProtoSynchronisation65Light(t *testing.T) { testMultiProtoSync(t, eth.ETH65, LightSync) }
-
func TestMultiProtoSynchronisation66Full(t *testing.T) { testMultiProtoSync(t, eth.ETH66, FullSync) }
func TestMultiProtoSynchronisation66Fast(t *testing.T) { testMultiProtoSync(t, eth.ETH66, FastSync) }
func TestMultiProtoSynchronisation66Light(t *testing.T) { testMultiProtoSync(t, eth.ETH66, LightSync) }
@@ -881,8 +840,8 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
chain := testChainBase.shorten(blockCacheMaxItems - 15)
// Create peers of every type
- tester.newPeer("peer 65", eth.ETH65, chain)
tester.newPeer("peer 66", eth.ETH66, chain)
+ //tester.newPeer("peer 65", eth.ETH67, chain)
// Synchronise with the requested peer and make sure all blocks were retrieved
if err := tester.sync(fmt.Sprintf("peer %d", protocol), nil, mode); err != nil {
@@ -891,7 +850,7 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
assertOwnChain(t, tester, chain.len())
// Check that no peers have been dropped off
- for _, version := range []int{65, 66} {
+ for _, version := range []int{66} {
peer := fmt.Sprintf("peer %d", version)
if _, ok := tester.peers[peer]; !ok {
t.Errorf("%s dropped", peer)
@@ -901,10 +860,6 @@ func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
// Tests that if a block is empty (e.g. header only), no body request should be
// made, and instead the header should be assembled into a whole block in itself.
-func TestEmptyShortCircuit65Full(t *testing.T) { testEmptyShortCircuit(t, eth.ETH65, FullSync) }
-func TestEmptyShortCircuit65Fast(t *testing.T) { testEmptyShortCircuit(t, eth.ETH65, FastSync) }
-func TestEmptyShortCircuit65Light(t *testing.T) { testEmptyShortCircuit(t, eth.ETH65, LightSync) }
-
func TestEmptyShortCircuit66Full(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, FullSync) }
func TestEmptyShortCircuit66Fast(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, FastSync) }
func TestEmptyShortCircuit66Light(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, LightSync) }
@@ -955,10 +910,6 @@ func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) {
// Tests that headers are enqueued continuously, preventing malicious nodes from
// stalling the downloader by feeding gapped header chains.
-func TestMissingHeaderAttack65Full(t *testing.T) { testMissingHeaderAttack(t, eth.ETH65, FullSync) }
-func TestMissingHeaderAttack65Fast(t *testing.T) { testMissingHeaderAttack(t, eth.ETH65, FastSync) }
-func TestMissingHeaderAttack65Light(t *testing.T) { testMissingHeaderAttack(t, eth.ETH65, LightSync) }
-
func TestMissingHeaderAttack66Full(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, FullSync) }
func TestMissingHeaderAttack66Fast(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, FastSync) }
func TestMissingHeaderAttack66Light(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, LightSync) }
@@ -987,10 +938,6 @@ func testMissingHeaderAttack(t *testing.T, protocol uint, mode SyncMode) {
// Tests that if requested headers are shifted (i.e. first is missing), the queue
// detects the invalid numbering.
-func TestShiftedHeaderAttack65Full(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH65, FullSync) }
-func TestShiftedHeaderAttack65Fast(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH65, FastSync) }
-func TestShiftedHeaderAttack65Light(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH65, LightSync) }
-
func TestShiftedHeaderAttack66Full(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, FullSync) }
func TestShiftedHeaderAttack66Fast(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, FastSync) }
func TestShiftedHeaderAttack66Light(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, LightSync) }
@@ -1024,7 +971,6 @@ func testShiftedHeaderAttack(t *testing.T, protocol uint, mode SyncMode) {
// Tests that upon detecting an invalid header, the recent ones are rolled back
// for various failure scenarios. Afterwards a full sync is attempted to make
// sure no state was corrupted.
-func TestInvalidHeaderRollback65Fast(t *testing.T) { testInvalidHeaderRollback(t, eth.ETH65, FastSync) }
func TestInvalidHeaderRollback66Fast(t *testing.T) { testInvalidHeaderRollback(t, eth.ETH66, FastSync) }
func testInvalidHeaderRollback(t *testing.T, protocol uint, mode SyncMode) {
@@ -1115,16 +1061,6 @@ func testInvalidHeaderRollback(t *testing.T, protocol uint, mode SyncMode) {
// Tests that a peer advertising a high TD doesn't get to stall the downloader
// afterwards by not sending any useful hashes.
-func TestHighTDStarvationAttack65Full(t *testing.T) {
- testHighTDStarvationAttack(t, eth.ETH65, FullSync)
-}
-func TestHighTDStarvationAttack65Fast(t *testing.T) {
- testHighTDStarvationAttack(t, eth.ETH65, FastSync)
-}
-func TestHighTDStarvationAttack65Light(t *testing.T) {
- testHighTDStarvationAttack(t, eth.ETH65, LightSync)
-}
-
func TestHighTDStarvationAttack66Full(t *testing.T) {
testHighTDStarvationAttack(t, eth.ETH66, FullSync)
}
@@ -1149,7 +1085,6 @@ func testHighTDStarvationAttack(t *testing.T, protocol uint, mode SyncMode) {
}
// Tests that misbehaving peers are disconnected, whilst behaving ones are not.
-func TestBlockHeaderAttackerDropping65(t *testing.T) { testBlockHeaderAttackerDropping(t, eth.ETH65) }
func TestBlockHeaderAttackerDropping66(t *testing.T) { testBlockHeaderAttackerDropping(t, eth.ETH66) }
func testBlockHeaderAttackerDropping(t *testing.T, protocol uint) {
@@ -1202,10 +1137,6 @@ func testBlockHeaderAttackerDropping(t *testing.T, protocol uint) {
// Tests that synchronisation progress (origin block number, current block number
// and highest block number) is tracked and updated correctly.
-func TestSyncProgress65Full(t *testing.T) { testSyncProgress(t, eth.ETH65, FullSync) }
-func TestSyncProgress65Fast(t *testing.T) { testSyncProgress(t, eth.ETH65, FastSync) }
-func TestSyncProgress65Light(t *testing.T) { testSyncProgress(t, eth.ETH65, LightSync) }
-
func TestSyncProgress66Full(t *testing.T) { testSyncProgress(t, eth.ETH66, FullSync) }
func TestSyncProgress66Fast(t *testing.T) { testSyncProgress(t, eth.ETH66, FastSync) }
func TestSyncProgress66Light(t *testing.T) { testSyncProgress(t, eth.ETH66, LightSync) }
@@ -1286,10 +1217,6 @@ func checkProgress(t *testing.T, d *Downloader, stage string, want ethereum.Sync
// Tests that synchronisation progress (origin block number and highest block
// number) is tracked and updated correctly in case of a fork (or manual head
// revertal).
-func TestForkedSyncProgress65Full(t *testing.T) { testForkedSyncProgress(t, eth.ETH65, FullSync) }
-func TestForkedSyncProgress65Fast(t *testing.T) { testForkedSyncProgress(t, eth.ETH65, FastSync) }
-func TestForkedSyncProgress65Light(t *testing.T) { testForkedSyncProgress(t, eth.ETH65, LightSync) }
-
func TestForkedSyncProgress66Full(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, FullSync) }
func TestForkedSyncProgress66Fast(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, FastSync) }
func TestForkedSyncProgress66Light(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, LightSync) }
@@ -1362,10 +1289,6 @@ func testForkedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
// Tests that if synchronisation is aborted due to some failure, then the progress
// origin is not updated in the next sync cycle, as it should be considered the
// continuation of the previous sync and not a new instance.
-func TestFailedSyncProgress65Full(t *testing.T) { testFailedSyncProgress(t, eth.ETH65, FullSync) }
-func TestFailedSyncProgress65Fast(t *testing.T) { testFailedSyncProgress(t, eth.ETH65, FastSync) }
-func TestFailedSyncProgress65Light(t *testing.T) { testFailedSyncProgress(t, eth.ETH65, LightSync) }
-
func TestFailedSyncProgress66Full(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, FullSync) }
func TestFailedSyncProgress66Fast(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, FastSync) }
func TestFailedSyncProgress66Light(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, LightSync) }
@@ -1435,10 +1358,6 @@ func testFailedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
// Tests that if an attacker fakes a chain height, after the attack is detected,
// the progress height is successfully reduced at the next sync invocation.
-func TestFakedSyncProgress65Full(t *testing.T) { testFakedSyncProgress(t, eth.ETH65, FullSync) }
-func TestFakedSyncProgress65Fast(t *testing.T) { testFakedSyncProgress(t, eth.ETH65, FastSync) }
-func TestFakedSyncProgress65Light(t *testing.T) { testFakedSyncProgress(t, eth.ETH65, LightSync) }
-
func TestFakedSyncProgress66Full(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, FullSync) }
func TestFakedSyncProgress66Fast(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, FastSync) }
func TestFakedSyncProgress66Light(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, LightSync) }
@@ -1512,10 +1431,6 @@ func testFakedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
// This test reproduces an issue where unexpected deliveries would
// block indefinitely if they arrived at the right time.
-func TestDeliverHeadersHang65Full(t *testing.T) { testDeliverHeadersHang(t, eth.ETH65, FullSync) }
-func TestDeliverHeadersHang65Fast(t *testing.T) { testDeliverHeadersHang(t, eth.ETH65, FastSync) }
-func TestDeliverHeadersHang65Light(t *testing.T) { testDeliverHeadersHang(t, eth.ETH65, LightSync) }
-
func TestDeliverHeadersHang66Full(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, FullSync) }
func TestDeliverHeadersHang66Fast(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, FastSync) }
func TestDeliverHeadersHang66Light(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, LightSync) }
@@ -1673,12 +1588,6 @@ func TestRemoteHeaderRequestSpan(t *testing.T) {
// Tests that peers below a pre-configured checkpoint block are prevented from
// being fast-synced from, avoiding potential cheap eclipse attacks.
-func TestCheckpointEnforcement65Full(t *testing.T) { testCheckpointEnforcement(t, eth.ETH65, FullSync) }
-func TestCheckpointEnforcement65Fast(t *testing.T) { testCheckpointEnforcement(t, eth.ETH65, FastSync) }
-func TestCheckpointEnforcement65Light(t *testing.T) {
- testCheckpointEnforcement(t, eth.ETH65, LightSync)
-}
-
func TestCheckpointEnforcement66Full(t *testing.T) { testCheckpointEnforcement(t, eth.ETH66, FullSync) }
func TestCheckpointEnforcement66Fast(t *testing.T) { testCheckpointEnforcement(t, eth.ETH66, FastSync) }
func TestCheckpointEnforcement66Light(t *testing.T) {
diff --git a/eth/downloader/peer.go b/eth/downloader/peer.go
index 066a366315..8632948329 100644
--- a/eth/downloader/peer.go
+++ b/eth/downloader/peer.go
@@ -413,7 +413,7 @@ func (ps *peerSet) HeaderIdlePeers() ([]*peerConnection, int) {
throughput := func(p *peerConnection) int {
return p.rates.Capacity(eth.BlockHeadersMsg, time.Second)
}
- return ps.idlePeers(eth.ETH65, eth.ETH66, idle, throughput)
+ return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput)
}
// BodyIdlePeers retrieves a flat list of all the currently body-idle peers within
@@ -425,7 +425,7 @@ func (ps *peerSet) BodyIdlePeers() ([]*peerConnection, int) {
throughput := func(p *peerConnection) int {
return p.rates.Capacity(eth.BlockBodiesMsg, time.Second)
}
- return ps.idlePeers(eth.ETH65, eth.ETH66, idle, throughput)
+ return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput)
}
// ReceiptIdlePeers retrieves a flat list of all the currently receipt-idle peers
@@ -437,7 +437,7 @@ func (ps *peerSet) ReceiptIdlePeers() ([]*peerConnection, int) {
throughput := func(p *peerConnection) int {
return p.rates.Capacity(eth.ReceiptsMsg, time.Second)
}
- return ps.idlePeers(eth.ETH65, eth.ETH66, idle, throughput)
+ return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput)
}
// NodeDataIdlePeers retrieves a flat list of all the currently node-data-idle
@@ -449,7 +449,7 @@ func (ps *peerSet) NodeDataIdlePeers() ([]*peerConnection, int) {
throughput := func(p *peerConnection) int {
return p.rates.Capacity(eth.NodeDataMsg, time.Second)
}
- return ps.idlePeers(eth.ETH65, eth.ETH66, idle, throughput)
+ return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput)
}
// idlePeers retrieves a flat list of all currently idle peers satisfying the
diff --git a/eth/ethconfig/config.go b/eth/ethconfig/config.go
index 89cdb75597..29b47af259 100644
--- a/eth/ethconfig/config.go
+++ b/eth/ethconfig/config.go
@@ -87,10 +87,11 @@ var Defaults = Config{
GasPrice: big.NewInt(params.GWei),
Recommit: 3 * time.Second,
},
- TxPool: core.DefaultTxPoolConfig,
- RPCGasCap: 50000000,
- GPO: FullNodeGPO,
- RPCTxFeeCap: 1, // 1 ether
+ TxPool: core.DefaultTxPoolConfig,
+ RPCGasCap: 50000000,
+ RPCEVMTimeout: 5 * time.Second,
+ GPO: FullNodeGPO,
+ RPCTxFeeCap: 1, // 1 ether
}
func init() {
@@ -188,6 +189,9 @@ type Config struct {
// RPCGasCap is the global gas cap for eth-call variants.
RPCGasCap uint64
+ // RPCEVMTimeout is the global timeout for eth-call.
+ RPCEVMTimeout time.Duration
+
// RPCTxFeeCap is the global transaction fee(price * gaslimit) cap for
// send-transction variants. The unit is ether.
RPCTxFeeCap float64
@@ -198,8 +202,8 @@ type Config struct {
// CheckpointOracle is the configuration for checkpoint oracle.
CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
- // Berlin block override (TODO: remove after the fork)
- OverrideLondon *big.Int `toml:",omitempty"`
+ // Arrow Glacier block override (TODO: remove after the fork)
+ OverrideArrowGlacier *big.Int `toml:",omitempty"`
}
// CreateConsensusEngine creates a consensus engine for the given chain configuration.
diff --git a/eth/ethconfig/gen_config.go b/eth/ethconfig/gen_config.go
index 2310dd4499..1f1ee3aafb 100644
--- a/eth/ethconfig/gen_config.go
+++ b/eth/ethconfig/gen_config.go
@@ -55,10 +55,11 @@ func (c Config) MarshalTOML() (interface{}, error) {
EnablePreimageRecording bool
DocRoot string `toml:"-"`
RPCGasCap uint64
+ RPCEVMTimeout time.Duration
RPCTxFeeCap float64
Checkpoint *params.TrustedCheckpoint `toml:",omitempty"`
CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
- OverrideLondon *big.Int `toml:",omitempty"`
+ OverrideArrowGlacier *big.Int `toml:",omitempty"`
}
var enc Config
enc.Genesis = c.Genesis
@@ -98,10 +99,11 @@ func (c Config) MarshalTOML() (interface{}, error) {
enc.EnablePreimageRecording = c.EnablePreimageRecording
enc.DocRoot = c.DocRoot
enc.RPCGasCap = c.RPCGasCap
+ enc.RPCEVMTimeout = c.RPCEVMTimeout
enc.RPCTxFeeCap = c.RPCTxFeeCap
enc.Checkpoint = c.Checkpoint
enc.CheckpointOracle = c.CheckpointOracle
- enc.OverrideLondon = c.OverrideLondon
+ enc.OverrideArrowGlacier = c.OverrideArrowGlacier
return &enc, nil
}
@@ -145,10 +147,11 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
EnablePreimageRecording *bool
DocRoot *string `toml:"-"`
RPCGasCap *uint64
+ RPCEVMTimeout *time.Duration
RPCTxFeeCap *float64
Checkpoint *params.TrustedCheckpoint `toml:",omitempty"`
CheckpointOracle *params.CheckpointOracleConfig `toml:",omitempty"`
- OverrideLondon *big.Int `toml:",omitempty"`
+ OverrideArrowGlacier *big.Int `toml:",omitempty"`
}
var dec Config
if err := unmarshal(&dec); err != nil {
@@ -265,6 +268,9 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
if dec.RPCGasCap != nil {
c.RPCGasCap = *dec.RPCGasCap
}
+ if dec.RPCEVMTimeout != nil {
+ c.RPCEVMTimeout = *dec.RPCEVMTimeout
+ }
if dec.RPCTxFeeCap != nil {
c.RPCTxFeeCap = *dec.RPCTxFeeCap
}
@@ -274,8 +280,8 @@ func (c *Config) UnmarshalTOML(unmarshal func(interface{}) error) error {
if dec.CheckpointOracle != nil {
c.CheckpointOracle = dec.CheckpointOracle
}
- if dec.OverrideLondon != nil {
- c.OverrideLondon = dec.OverrideLondon
+ if dec.OverrideArrowGlacier != nil {
+ c.OverrideArrowGlacier = dec.OverrideArrowGlacier
}
return nil
}
diff --git a/eth/fetcher/block_fetcher.go b/eth/fetcher/block_fetcher.go
index 45983c97ce..7624268a7f 100644
--- a/eth/fetcher/block_fetcher.go
+++ b/eth/fetcher/block_fetcher.go
@@ -391,13 +391,14 @@ func (f *BlockFetcher) loop() {
blockAnnounceDOSMeter.Mark(1)
break
}
+ if notification.number == 0 {
+ break
+ }
// If we have a valid block number, check that it's potentially useful
- if notification.number > 0 {
- if dist := int64(notification.number) - int64(f.chainHeight()); dist < -maxUncleDist || dist > maxQueueDist {
- log.Debug("Peer discarded announcement", "peer", notification.origin, "number", notification.number, "hash", notification.hash, "distance", dist)
- blockAnnounceDropMeter.Mark(1)
- break
- }
+ if dist := int64(notification.number) - int64(f.chainHeight()); dist < -maxUncleDist || dist > maxQueueDist {
+ log.Debug("Peer discarded announcement", "peer", notification.origin, "number", notification.number, "hash", notification.hash, "distance", dist)
+ blockAnnounceDropMeter.Mark(1)
+ break
}
// All is well, schedule the announce if block's not yet downloading
if _, ok := f.fetching[notification.hash]; ok {
diff --git a/eth/fetcher/tx_fetcher.go b/eth/fetcher/tx_fetcher.go
index 3ba7753916..b10c0db9ee 100644
--- a/eth/fetcher/tx_fetcher.go
+++ b/eth/fetcher/tx_fetcher.go
@@ -18,6 +18,7 @@ package fetcher
import (
"bytes"
+ "errors"
"fmt"
mrand "math/rand"
"sort"
@@ -277,29 +278,27 @@ func (f *TxFetcher) Enqueue(peer string, txs []*types.Transaction, direct bool)
)
errs := f.addTxs(txs)
for i, err := range errs {
- if err != nil {
- // Track the transaction hash if the price is too low for us.
- // Avoid re-request this transaction when we receive another
- // announcement.
- if err == core.ErrUnderpriced || err == core.ErrReplaceUnderpriced {
- for f.underpriced.Cardinality() >= maxTxUnderpricedSetSize {
- f.underpriced.Pop()
- }
- f.underpriced.Add(txs[i].Hash())
+ // Track the transaction hash if the price is too low for us.
+ // Avoid re-request this transaction when we receive another
+ // announcement.
+ if errors.Is(err, core.ErrUnderpriced) || errors.Is(err, core.ErrReplaceUnderpriced) {
+ for f.underpriced.Cardinality() >= maxTxUnderpricedSetSize {
+ f.underpriced.Pop()
}
- // Track a few interesting failure types
- switch err {
- case nil: // Noop, but need to handle to not count these
+ f.underpriced.Add(txs[i].Hash())
+ }
+ // Track a few interesting failure types
+ switch {
+ case err == nil: // Noop, but need to handle to not count these
- case core.ErrAlreadyKnown:
- duplicate++
+ case errors.Is(err, core.ErrAlreadyKnown):
+ duplicate++
- case core.ErrUnderpriced, core.ErrReplaceUnderpriced:
- underpriced++
+ case errors.Is(err, core.ErrUnderpriced) || errors.Is(err, core.ErrReplaceUnderpriced):
+ underpriced++
- default:
- otherreject++
- }
+ default:
+ otherreject++
}
added = append(added, txs[i].Hash())
}
diff --git a/eth/filters/bench_test.go b/eth/filters/bench_test.go
index 020db070e5..9632f4195f 100644
--- a/eth/filters/bench_test.go
+++ b/eth/filters/bench_test.go
@@ -62,6 +62,7 @@ func BenchmarkBloomBits32k(b *testing.B) {
const benchFilterCnt = 2000
func benchmarkBloomBits(b *testing.B, sectionSize uint64) {
+ b.Skip("test disabled: this tests presume (and modify) an existing datadir.")
benchDataDir := node.DefaultDataDir() + "/geth/chaindata"
b.Log("Running bloombits benchmark section size:", sectionSize)
@@ -155,6 +156,7 @@ func clearBloomBits(db ethdb.Database) {
}
func BenchmarkNoBloomBits(b *testing.B) {
+ b.Skip("test disabled: this tests presume (and modify) an existing datadir.")
benchDataDir := node.DefaultDataDir() + "/geth/chaindata"
b.Log("Running benchmark without bloombits")
db, err := rawdb.NewLevelDBDatabase(benchDataDir, 128, 1024, "", false)
diff --git a/eth/filters/filter_system_test.go b/eth/filters/filter_system_test.go
index d55674f309..efeab58d7d 100644
--- a/eth/filters/filter_system_test.go
+++ b/eth/filters/filter_system_test.go
@@ -506,58 +506,80 @@ func TestPendingLogsSubscription(t *testing.T) {
},
}
+ pendingBlockNumber = big.NewInt(rpc.PendingBlockNumber.Int64())
+
testCases = []struct {
crit ethereum.FilterQuery
expected []*types.Log
c chan []*types.Log
sub *Subscription
+ err chan error
}{
// match all
{
- ethereum.FilterQuery{}, flattenLogs(allLogs),
- nil, nil,
+ ethereum.FilterQuery{FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber},
+ flattenLogs(allLogs),
+ nil, nil, nil,
},
// match none due to no matching addresses
{
- ethereum.FilterQuery{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{nil}},
+ ethereum.FilterQuery{Addresses: []common.Address{{}, notUsedAddress}, Topics: [][]common.Hash{nil}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber},
nil,
- nil, nil,
+ nil, nil, nil,
},
// match logs based on addresses, ignore topics
{
- ethereum.FilterQuery{Addresses: []common.Address{firstAddr}},
+ ethereum.FilterQuery{Addresses: []common.Address{firstAddr}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber},
append(flattenLogs(allLogs[:2]), allLogs[5][3]),
- nil, nil,
+ nil, nil, nil,
},
// match none due to no matching topics (match with address)
{
- ethereum.FilterQuery{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}},
+ ethereum.FilterQuery{Addresses: []common.Address{secondAddr}, Topics: [][]common.Hash{{notUsedTopic}}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber},
+ nil,
nil, nil, nil,
},
// match logs based on addresses and topics
{
- ethereum.FilterQuery{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}},
+ ethereum.FilterQuery{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber},
append(flattenLogs(allLogs[3:5]), allLogs[5][0]),
- nil, nil,
+ nil, nil, nil,
},
// match logs based on multiple addresses and "or" topics
{
- ethereum.FilterQuery{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}},
+ ethereum.FilterQuery{Addresses: []common.Address{secondAddr, thirdAddress}, Topics: [][]common.Hash{{firstTopic, secondTopic}}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber},
append(flattenLogs(allLogs[2:5]), allLogs[5][0]),
- nil,
- nil,
- },
- // block numbers are ignored for filters created with New***Filter, these return all logs that match the given criteria when the state changes
- {
- ethereum.FilterQuery{Addresses: []common.Address{firstAddr}, FromBlock: big.NewInt(2), ToBlock: big.NewInt(3)},
- append(flattenLogs(allLogs[:2]), allLogs[5][3]),
- nil, nil,
+ nil, nil, nil,
},
// multiple pending logs, should match only 2 topics from the logs in block 5
{
- ethereum.FilterQuery{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, fourthTopic}}},
+ ethereum.FilterQuery{Addresses: []common.Address{thirdAddress}, Topics: [][]common.Hash{{firstTopic, fourthTopic}}, FromBlock: pendingBlockNumber, ToBlock: pendingBlockNumber},
[]*types.Log{allLogs[5][0], allLogs[5][2]},
- nil, nil,
+ nil, nil, nil,
+ },
+ // match none due to only matching new mined logs
+ {
+ ethereum.FilterQuery{},
+ nil,
+ nil, nil, nil,
+ },
+ // match none due to only matching mined logs within a specific block range
+ {
+ ethereum.FilterQuery{FromBlock: big.NewInt(1), ToBlock: big.NewInt(2)},
+ nil,
+ nil, nil, nil,
+ },
+ // match all due to matching mined and pending logs
+ {
+ ethereum.FilterQuery{FromBlock: big.NewInt(rpc.LatestBlockNumber.Int64()), ToBlock: big.NewInt(rpc.PendingBlockNumber.Int64())},
+ flattenLogs(allLogs),
+ nil, nil, nil,
+ },
+ // match none due to matching logs from a specific block number to new mined blocks
+ {
+ ethereum.FilterQuery{FromBlock: big.NewInt(1), ToBlock: big.NewInt(rpc.LatestBlockNumber.Int64())},
+ nil,
+ nil, nil, nil,
},
}
)
@@ -567,43 +589,69 @@ func TestPendingLogsSubscription(t *testing.T) {
// (some) events are posted.
for i := range testCases {
testCases[i].c = make(chan []*types.Log)
- testCases[i].sub, _ = api.events.SubscribeLogs(testCases[i].crit, testCases[i].c)
+ testCases[i].err = make(chan error)
+
+ var err error
+ testCases[i].sub, err = api.events.SubscribeLogs(testCases[i].crit, testCases[i].c)
+ if err != nil {
+ t.Fatalf("SubscribeLogs %d failed: %v\n", i, err)
+ }
}
for n, test := range testCases {
i := n
tt := test
go func() {
+ defer tt.sub.Unsubscribe()
+
var fetched []*types.Log
+
+ timeout := time.After(1 * time.Second)
fetchLoop:
for {
- logs := <-tt.c
- fetched = append(fetched, logs...)
- if len(fetched) >= len(tt.expected) {
+ select {
+ case logs := <-tt.c:
+ // Do not break early if we've fetched greater, or equal,
+ // to the number of logs expected. This ensures we do not
+ // deadlock the filter system because it will do a blocking
+ // send on this channel if another log arrives.
+ fetched = append(fetched, logs...)
+ case <-timeout:
break fetchLoop
}
}
if len(fetched) != len(tt.expected) {
- panic(fmt.Sprintf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched)))
+ tt.err <- fmt.Errorf("invalid number of logs for case %d, want %d log(s), got %d", i, len(tt.expected), len(fetched))
+ return
}
for l := range fetched {
if fetched[l].Removed {
- panic(fmt.Sprintf("expected log not to be removed for log %d in case %d", l, i))
+ tt.err <- fmt.Errorf("expected log not to be removed for log %d in case %d", l, i)
+ return
}
if !reflect.DeepEqual(fetched[l], tt.expected[l]) {
- panic(fmt.Sprintf("invalid log on index %d for case %d", l, i))
+ tt.err <- fmt.Errorf("invalid log on index %d for case %d\n", l, i)
+ return
}
}
+ tt.err <- nil
}()
}
// raise events
- time.Sleep(1 * time.Second)
for _, ev := range allLogs {
backend.pendingLogsFeed.Send(ev)
}
+
+ for i := range testCases {
+ err := <-testCases[i].err
+ if err != nil {
+ t.Fatalf("test %d failed: %v", i, err)
+ }
+ <-testCases[i].sub.Err()
+ }
}
// TestPendingTxFilterDeadlock tests if the event loop hangs when pending
diff --git a/eth/filters/filter_test.go b/eth/filters/filter_test.go
index fd25013cc6..63a48f762d 100644
--- a/eth/filters/filter_test.go
+++ b/eth/filters/filter_test.go
@@ -65,15 +65,19 @@ func BenchmarkFilters(b *testing.B) {
case 2403:
receipt := makeReceipt(addr1)
gen.AddUncheckedReceipt(receipt)
+ gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, gen.BaseFee(), nil))
case 1034:
receipt := makeReceipt(addr2)
gen.AddUncheckedReceipt(receipt)
+ gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, gen.BaseFee(), nil))
case 34:
receipt := makeReceipt(addr3)
gen.AddUncheckedReceipt(receipt)
+ gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, gen.BaseFee(), nil))
case 99999:
receipt := makeReceipt(addr4)
gen.AddUncheckedReceipt(receipt)
+ gen.AddUncheckedTx(types.NewTransaction(999, common.HexToAddress("0x999"), big.NewInt(999), 999, gen.BaseFee(), nil))
}
})
diff --git a/eth/gasprice/gasprice_test.go b/eth/gasprice/gasprice_test.go
index feecfddec7..2d394200a0 100644
--- a/eth/gasprice/gasprice_test.go
+++ b/eth/gasprice/gasprice_test.go
@@ -99,29 +99,28 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
var (
key, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
addr = crypto.PubkeyToAddress(key.PublicKey)
+ config = *params.TestChainConfig // needs copy because it is modified below
gspec = &core.Genesis{
- Config: params.TestChainConfig,
+ Config: &config,
Alloc: core.GenesisAlloc{addr: {Balance: big.NewInt(math.MaxInt64)}},
}
signer = types.LatestSigner(gspec.Config)
)
- if londonBlock != nil {
- gspec.Config.LondonBlock = londonBlock
- signer = types.LatestSigner(gspec.Config)
- } else {
- gspec.Config.LondonBlock = nil
- }
+ config.LondonBlock = londonBlock
+ config.ArrowGlacierBlock = londonBlock
engine := ethash.NewFaker()
db := rawdb.NewMemoryDatabase()
- genesis, _ := gspec.Commit(db)
-
+ genesis, err := gspec.Commit(db)
+ if err != nil {
+ t.Fatal(err)
+ }
// 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 tx *types.Transaction
+ var txdata types.TxData
if londonBlock != nil && b.Number().Cmp(londonBlock) >= 0 {
- txdata := &types.DynamicFeeTx{
+ txdata = &types.DynamicFeeTx{
ChainID: gspec.Config.ChainID,
Nonce: b.TxNonce(addr),
To: &common.Address{},
@@ -130,9 +129,8 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
GasTipCap: big.NewInt(int64(i+1) * params.GWei),
Data: []byte{},
}
- tx = types.NewTx(txdata)
} else {
- txdata := &types.LegacyTx{
+ txdata = &types.LegacyTx{
Nonce: b.TxNonce(addr),
To: &common.Address{},
Gas: 21000,
@@ -140,18 +138,13 @@ func newTestBackend(t *testing.T, londonBlock *big.Int, pending bool) *testBacke
Value: big.NewInt(100),
Data: []byte{},
}
- tx = types.NewTx(txdata)
}
- tx, err := types.SignTx(tx, signer, key)
- if err != nil {
- t.Fatalf("failed to create tx: %v", err)
- }
- b.AddTx(tx)
+ 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{}, nil, nil)
+ chain, err := core.NewBlockChain(diskdb, &core.CacheConfig{TrieCleanNoPrefetch: true}, &config, engine, vm.Config{}, nil, nil)
if err != nil {
t.Fatalf("Failed to create local chain, %v", err)
}
diff --git a/eth/handler.go b/eth/handler.go
index aff4871afa..41d89c5fb0 100644
--- a/eth/handler.go
+++ b/eth/handler.go
@@ -66,7 +66,7 @@ type txPool interface {
// Pending should return pending transactions.
// The slice should be modifiable by the caller.
- Pending(enforceTips bool) (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.
@@ -117,7 +117,6 @@ type handler struct {
whitelist map[uint64]common.Hash
// channels for fetcher, syncer, txsyncLoop
- txsyncCh chan *txsync
quitSync chan struct{}
chainSync *chainSyncer
@@ -140,7 +139,6 @@ func newHandler(config *handlerConfig) (*handler, error) {
chain: config.Chain,
peers: newPeerSet(),
whitelist: config.Whitelist,
- txsyncCh: make(chan *txsync),
quitSync: make(chan struct{}),
}
if config.Sync == downloader.FullSync {
@@ -408,9 +406,8 @@ func (h *handler) Start(maxPeers int) {
go h.minedBroadcastLoop()
// start sync handlers
- h.wg.Add(2)
+ h.wg.Add(1)
go h.chainSync.loop()
- go h.txsyncLoop64() // TODO(karalabe): Legacy initial tx echange, drop with eth/64.
}
func (h *handler) Stop() {
diff --git a/eth/handler_eth_test.go b/eth/handler_eth_test.go
index 038de46990..b8db5039c9 100644
--- a/eth/handler_eth_test.go
+++ b/eth/handler_eth_test.go
@@ -80,7 +80,6 @@ func (h *testEthHandler) Handle(peer *eth.Peer, packet eth.Packet) error {
// Tests that peers are correctly accepted (or rejected) based on the advertised
// fork IDs in the protocol handshake.
-func TestForkIDSplit65(t *testing.T) { testForkIDSplit(t, eth.ETH65) }
func TestForkIDSplit66(t *testing.T) { testForkIDSplit(t, eth.ETH66) }
func testForkIDSplit(t *testing.T, protocol uint) {
@@ -236,7 +235,6 @@ func testForkIDSplit(t *testing.T, protocol uint) {
}
// Tests that received transactions are added to the local pool.
-func TestRecvTransactions65(t *testing.T) { testRecvTransactions(t, eth.ETH65) }
func TestRecvTransactions66(t *testing.T) { testRecvTransactions(t, eth.ETH66) }
func testRecvTransactions(t *testing.T, protocol uint) {
@@ -294,7 +292,6 @@ func testRecvTransactions(t *testing.T, protocol uint) {
}
// This test checks that pending transactions are sent.
-func TestSendTransactions65(t *testing.T) { testSendTransactions(t, eth.ETH65) }
func TestSendTransactions66(t *testing.T) { testSendTransactions(t, eth.ETH66) }
func testSendTransactions(t *testing.T, protocol uint) {
@@ -306,7 +303,7 @@ func testSendTransactions(t *testing.T, protocol uint) {
insert := make([]*types.Transaction, 100)
for nonce := range insert {
- tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), make([]byte, txsyncPackSize/10))
+ tx := types.NewTransaction(uint64(nonce), common.Address{}, big.NewInt(0), 100000, big.NewInt(0), make([]byte, 10240))
tx, _ = types.SignTx(tx, types.HomesteadSigner{}, testKey)
insert[nonce] = tx
@@ -380,7 +377,6 @@ func testSendTransactions(t *testing.T, protocol uint) {
// Tests that transactions get propagated to all attached peers, either via direct
// broadcasts or via announcements/retrievals.
-func TestTransactionPropagation65(t *testing.T) { testTransactionPropagation(t, eth.ETH65) }
func TestTransactionPropagation66(t *testing.T) { testTransactionPropagation(t, eth.ETH66) }
func testTransactionPropagation(t *testing.T, protocol uint) {
@@ -490,7 +486,6 @@ func TestCheckpointChallenge(t *testing.T) {
}
func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpoint bool, timeout bool, empty bool, match bool, drop bool) {
- t.Parallel()
// Reduce the checkpoint handshake challenge timeout
defer func(old time.Duration) { syncChallengeTimeout = old }(syncChallengeTimeout)
@@ -521,8 +516,8 @@ func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpo
defer p2pLocal.Close()
defer p2pRemote.Close()
- local := eth.NewPeer(eth.ETH65, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pLocal), p2pLocal, handler.txpool)
- remote := eth.NewPeer(eth.ETH65, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pRemote), p2pRemote, handler.txpool)
+ local := eth.NewPeer(eth.ETH66, p2p.NewPeerPipe(enode.ID{1}, "", nil, p2pLocal), p2pLocal, handler.txpool)
+ remote := eth.NewPeer(eth.ETH66, p2p.NewPeerPipe(enode.ID{2}, "", nil, p2pRemote), p2pRemote, handler.txpool)
defer local.Close()
defer remote.Close()
@@ -543,30 +538,39 @@ func testCheckpointChallenge(t *testing.T, syncmode downloader.SyncMode, checkpo
if err := remote.Handshake(1, td, head.Hash(), genesis.Hash(), forkid.NewIDWithChain(handler.chain), forkid.NewFilter(handler.chain)); err != nil {
t.Fatalf("failed to run protocol handshake")
}
-
// Connect a new peer and check that we receive the checkpoint challenge.
if checkpoint {
- if err := remote.ExpectRequestHeadersByNumber(response.Number.Uint64(), 1, 0, false); err != nil {
- t.Fatalf("challenge mismatch: %v", err)
+ msg, err := p2pRemote.ReadMsg()
+ if err != nil {
+ t.Fatalf("failed to read checkpoint challenge: %v", err)
+ }
+ request := new(eth.GetBlockHeadersPacket66)
+ if err := msg.Decode(request); err != nil {
+ t.Fatalf("failed to decode checkpoint challenge: %v", err)
+ }
+ query := request.GetBlockHeadersPacket
+ if query.Origin.Number != response.Number.Uint64() || query.Amount != 1 || query.Skip != 0 || query.Reverse {
+ t.Fatalf("challenge mismatch: have [%d, %d, %d, %v] want [%d, %d, %d, %v]",
+ query.Origin.Number, query.Amount, query.Skip, query.Reverse,
+ response.Number.Uint64(), 1, 0, false)
}
// Create a block to reply to the challenge if no timeout is simulated.
if !timeout {
if empty {
- if err := remote.SendBlockHeaders([]*types.Header{}); err != nil {
+ if err := remote.ReplyBlockHeaders(request.RequestId, []*types.Header{}); err != nil {
t.Fatalf("failed to answer challenge: %v", err)
}
} else if match {
- if err := remote.SendBlockHeaders([]*types.Header{response}); err != nil {
+ if err := remote.ReplyBlockHeaders(request.RequestId, []*types.Header{response}); err != nil {
t.Fatalf("failed to answer challenge: %v", err)
}
} else {
- if err := remote.SendBlockHeaders([]*types.Header{{Number: response.Number}}); err != nil {
+ if err := remote.ReplyBlockHeaders(request.RequestId, []*types.Header{{Number: response.Number}}); err != nil {
t.Fatalf("failed to answer challenge: %v", err)
}
}
}
}
-
// Wait until the test timeout passes to ensure proper cleanup
time.Sleep(syncChallengeTimeout + 300*time.Millisecond)
@@ -619,8 +623,8 @@ func testBroadcastBlock(t *testing.T, peers, bcasts int) {
defer sourcePipe.Close()
defer sinkPipe.Close()
- sourcePeer := eth.NewPeer(eth.ETH65, p2p.NewPeerPipe(enode.ID{byte(i)}, "", nil, sourcePipe), sourcePipe, nil)
- sinkPeer := eth.NewPeer(eth.ETH65, p2p.NewPeerPipe(enode.ID{0}, "", nil, sinkPipe), sinkPipe, nil)
+ sourcePeer := eth.NewPeer(eth.ETH66, p2p.NewPeerPipe(enode.ID{byte(i)}, "", nil, sourcePipe), sourcePipe, nil)
+ sinkPeer := eth.NewPeer(eth.ETH66, p2p.NewPeerPipe(enode.ID{0}, "", nil, sinkPipe), sinkPipe, nil)
defer sourcePeer.Close()
defer sinkPeer.Close()
@@ -671,7 +675,6 @@ func testBroadcastBlock(t *testing.T, peers, bcasts int) {
// Tests that a propagated malformed block (uncles or transactions don't match
// with the hashes in the header) gets discarded and not broadcast forward.
-func TestBroadcastMalformedBlock65(t *testing.T) { testBroadcastMalformedBlock(t, eth.ETH65) }
func TestBroadcastMalformedBlock66(t *testing.T) { testBroadcastMalformedBlock(t, eth.ETH66) }
func testBroadcastMalformedBlock(t *testing.T, protocol uint) {
diff --git a/eth/handler_test.go b/eth/handler_test.go
index 090bd9239c..b2f00b7977 100644
--- a/eth/handler_test.go
+++ b/eth/handler_test.go
@@ -91,7 +91,7 @@ func (p *testTxPool) AddRemotes(txs []*types.Transaction) []error {
}
// Pending returns all the transactions known to the pool
-func (p *testTxPool) Pending(enforceTips bool) (map[common.Address]types.Transactions, error) {
+func (p *testTxPool) Pending(enforceTips bool) map[common.Address]types.Transactions {
p.lock.RLock()
defer p.lock.RUnlock()
@@ -103,7 +103,7 @@ func (p *testTxPool) Pending(enforceTips bool) (map[common.Address]types.Transac
for _, batch := range batches {
sort.Sort(types.TxByNonce(batch))
}
- return batches, nil
+ return batches
}
// SubscribeNewTxsEvent should return an event subscription of NewTxsEvent and
diff --git a/eth/protocols/eth/broadcast.go b/eth/protocols/eth/broadcast.go
index 328396d510..614e6361c6 100644
--- a/eth/protocols/eth/broadcast.go
+++ b/eth/protocols/eth/broadcast.go
@@ -75,18 +75,18 @@ func (p *Peer) broadcastTransactions() {
if done == nil && len(queue) > 0 {
// Pile transaction until we reach our allowed network limit
var (
- hashes []common.Hash
- txs []*types.Transaction
- size common.StorageSize
+ hashesCount uint64
+ txs []*types.Transaction
+ size common.StorageSize
)
for i := 0; i < len(queue) && size < maxTxPacketSize; i++ {
if tx := p.txpool.Get(queue[i]); tx != nil {
txs = append(txs, tx)
size += tx.Size()
}
- hashes = append(hashes, queue[i])
+ hashesCount++
}
- queue = queue[:copy(queue, queue[len(hashes):])]
+ queue = queue[:copy(queue, queue[hashesCount:])]
// If there's anything available to transfer, fire up an async writer
if len(txs) > 0 {
diff --git a/eth/protocols/eth/handler.go b/eth/protocols/eth/handler.go
index 6bbaa2f555..8289300141 100644
--- a/eth/protocols/eth/handler.go
+++ b/eth/protocols/eth/handler.go
@@ -171,39 +171,21 @@ type Decoder interface {
Time() time.Time
}
-var eth65 = map[uint64]msgHandler{
- GetBlockHeadersMsg: handleGetBlockHeaders,
- BlockHeadersMsg: handleBlockHeaders,
- GetBlockBodiesMsg: handleGetBlockBodies,
- BlockBodiesMsg: handleBlockBodies,
- GetNodeDataMsg: handleGetNodeData,
- NodeDataMsg: handleNodeData,
- GetReceiptsMsg: handleGetReceipts,
- ReceiptsMsg: handleReceipts,
- NewBlockHashesMsg: handleNewBlockhashes,
- NewBlockMsg: handleNewBlock,
- TransactionsMsg: handleTransactions,
- NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes,
- GetPooledTransactionsMsg: handleGetPooledTransactions,
- PooledTransactionsMsg: handlePooledTransactions,
-}
-
var eth66 = map[uint64]msgHandler{
NewBlockHashesMsg: handleNewBlockhashes,
NewBlockMsg: handleNewBlock,
TransactionsMsg: handleTransactions,
NewPooledTransactionHashesMsg: handleNewPooledTransactionHashes,
- // eth66 messages with request-id
- GetBlockHeadersMsg: handleGetBlockHeaders66,
- BlockHeadersMsg: handleBlockHeaders66,
- GetBlockBodiesMsg: handleGetBlockBodies66,
- BlockBodiesMsg: handleBlockBodies66,
- GetNodeDataMsg: handleGetNodeData66,
- NodeDataMsg: handleNodeData66,
- GetReceiptsMsg: handleGetReceipts66,
- ReceiptsMsg: handleReceipts66,
- GetPooledTransactionsMsg: handleGetPooledTransactions66,
- PooledTransactionsMsg: handlePooledTransactions66,
+ GetBlockHeadersMsg: handleGetBlockHeaders66,
+ BlockHeadersMsg: handleBlockHeaders66,
+ GetBlockBodiesMsg: handleGetBlockBodies66,
+ BlockBodiesMsg: handleBlockBodies66,
+ GetNodeDataMsg: handleGetNodeData66,
+ NodeDataMsg: handleNodeData66,
+ GetReceiptsMsg: handleGetReceipts66,
+ ReceiptsMsg: handleReceipts66,
+ GetPooledTransactionsMsg: handleGetPooledTransactions66,
+ PooledTransactionsMsg: handlePooledTransactions66,
}
// handleMessage is invoked whenever an inbound message is received from a remote
@@ -219,10 +201,11 @@ func handleMessage(backend Backend, peer *Peer) error {
}
defer msg.Discard()
- var handlers = eth65
- if peer.Version() >= ETH66 {
- handlers = eth66
- }
+ var handlers = eth66
+ //if peer.Version() >= ETH67 { // Left in as a sample when new protocol is added
+ // handlers = eth67
+ //}
+
// Track the amount of time it takes to serve the request and run the handler
if metrics.Enabled {
h := fmt.Sprintf("%s/%s/%d/%#02x", p2p.HandleHistName, ProtocolName, peer.Version(), msg.Code)
diff --git a/eth/protocols/eth/handler_test.go b/eth/protocols/eth/handler_test.go
index 473be3f9b7..66f0134096 100644
--- a/eth/protocols/eth/handler_test.go
+++ b/eth/protocols/eth/handler_test.go
@@ -110,7 +110,6 @@ func (b *testBackend) Handle(*Peer, Packet) error {
}
// Tests that block headers can be retrieved from a remote chain based on user queries.
-func TestGetBlockHeaders65(t *testing.T) { testGetBlockHeaders(t, ETH65) }
func TestGetBlockHeaders66(t *testing.T) { testGetBlockHeaders(t, ETH66) }
func testGetBlockHeaders(t *testing.T, protocol uint) {
@@ -127,6 +126,12 @@ func testGetBlockHeaders(t *testing.T, protocol uint) {
for i := range unknown {
unknown[i] = byte(i)
}
+ getHashes := func(from, limit uint64) (hashes []common.Hash) {
+ for i := uint64(0); i < limit; i++ {
+ hashes = append(hashes, backend.chain.GetCanonicalHash(from-1-i))
+ }
+ return hashes
+ }
// Create a batch of tests for various scenarios
limit := uint64(maxHeadersServe)
tests := []struct {
@@ -184,7 +189,7 @@ func testGetBlockHeaders(t *testing.T, protocol uint) {
// Ensure protocol limits are honored
{
&GetBlockHeadersPacket{Origin: HashOrNumber{Number: backend.chain.CurrentBlock().NumberU64() - 1}, Amount: limit + 10, Reverse: true},
- backend.chain.GetBlockHashesFromHash(backend.chain.CurrentBlock().Hash(), limit),
+ getHashes(backend.chain.CurrentBlock().NumberU64(), limit),
},
// Check that requesting more than available is handled gracefully
{
@@ -254,44 +259,30 @@ func testGetBlockHeaders(t *testing.T, protocol uint) {
headers = append(headers, backend.chain.GetBlockByHash(hash).Header())
}
// Send the hash request and verify the response
- if protocol <= ETH65 {
- p2p.Send(peer.app, GetBlockHeadersMsg, tt.query)
- if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, headers); err != nil {
- t.Errorf("test %d: headers mismatch: %v", i, err)
- }
- } else {
- p2p.Send(peer.app, GetBlockHeadersMsg, GetBlockHeadersPacket66{
- RequestId: 123,
- GetBlockHeadersPacket: tt.query,
- })
- if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, BlockHeadersPacket66{
- RequestId: 123,
- BlockHeadersPacket: headers,
- }); err != nil {
- t.Errorf("test %d: headers mismatch: %v", i, err)
- }
+ p2p.Send(peer.app, GetBlockHeadersMsg, GetBlockHeadersPacket66{
+ RequestId: 123,
+ GetBlockHeadersPacket: tt.query,
+ })
+ if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, BlockHeadersPacket66{
+ RequestId: 123,
+ BlockHeadersPacket: headers,
+ }); err != nil {
+ t.Errorf("test %d: headers mismatch: %v", i, err)
}
// If the test used number origins, repeat with hashes as the too
if tt.query.Origin.Hash == (common.Hash{}) {
if origin := backend.chain.GetBlockByNumber(tt.query.Origin.Number); origin != nil {
tt.query.Origin.Hash, tt.query.Origin.Number = origin.Hash(), 0
- if protocol <= ETH65 {
- p2p.Send(peer.app, GetBlockHeadersMsg, tt.query)
- if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, headers); err != nil {
- t.Errorf("test %d: headers mismatch: %v", i, err)
- }
- } else {
- p2p.Send(peer.app, GetBlockHeadersMsg, GetBlockHeadersPacket66{
- RequestId: 456,
- GetBlockHeadersPacket: tt.query,
- })
- if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, BlockHeadersPacket66{
- RequestId: 456,
- BlockHeadersPacket: headers,
- }); err != nil {
- t.Errorf("test %d: headers mismatch: %v", i, err)
- }
+ p2p.Send(peer.app, GetBlockHeadersMsg, GetBlockHeadersPacket66{
+ RequestId: 456,
+ GetBlockHeadersPacket: tt.query,
+ })
+ if err := p2p.ExpectMsg(peer.app, BlockHeadersMsg, BlockHeadersPacket66{
+ RequestId: 456,
+ BlockHeadersPacket: headers,
+ }); err != nil {
+ t.Errorf("test %d: headers mismatch: %v", i, err)
}
}
}
@@ -299,7 +290,6 @@ func testGetBlockHeaders(t *testing.T, protocol uint) {
}
// Tests that block contents can be retrieved from a remote chain based on their hashes.
-func TestGetBlockBodies65(t *testing.T) { testGetBlockBodies(t, ETH65) }
func TestGetBlockBodies66(t *testing.T) { testGetBlockBodies(t, ETH66) }
func testGetBlockBodies(t *testing.T, protocol uint) {
@@ -369,28 +359,20 @@ func testGetBlockBodies(t *testing.T, protocol uint) {
}
}
// Send the hash request and verify the response
- if protocol <= ETH65 {
- p2p.Send(peer.app, GetBlockBodiesMsg, hashes)
- if err := p2p.ExpectMsg(peer.app, BlockBodiesMsg, bodies); err != nil {
- t.Errorf("test %d: bodies mismatch: %v", i, err)
- }
- } else {
- p2p.Send(peer.app, GetBlockBodiesMsg, GetBlockBodiesPacket66{
- RequestId: 123,
- GetBlockBodiesPacket: hashes,
- })
- if err := p2p.ExpectMsg(peer.app, BlockBodiesMsg, BlockBodiesPacket66{
- RequestId: 123,
- BlockBodiesPacket: bodies,
- }); err != nil {
- t.Errorf("test %d: bodies mismatch: %v", i, err)
- }
+ p2p.Send(peer.app, GetBlockBodiesMsg, GetBlockBodiesPacket66{
+ RequestId: 123,
+ GetBlockBodiesPacket: hashes,
+ })
+ if err := p2p.ExpectMsg(peer.app, BlockBodiesMsg, BlockBodiesPacket66{
+ RequestId: 123,
+ BlockBodiesPacket: bodies,
+ }); err != nil {
+ t.Errorf("test %d: bodies mismatch: %v", i, err)
}
}
}
// Tests that the state trie nodes can be retrieved based on hashes.
-func TestGetNodeData65(t *testing.T) { testGetNodeData(t, ETH65) }
func TestGetNodeData66(t *testing.T) { testGetNodeData(t, ETH66) }
func testGetNodeData(t *testing.T, protocol uint) {
@@ -403,7 +385,7 @@ func testGetNodeData(t *testing.T, protocol uint) {
acc2Addr := crypto.PubkeyToAddress(acc2Key.PublicKey)
signer := types.HomesteadSigner{}
- // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_markets_test)
+ // Create a chain generator with some simple transactions (blatantly stolen from @fjl/chain_makers_test)
generator := func(i int, block *core.BlockGen) {
switch i {
case 0:
@@ -438,9 +420,8 @@ func testGetNodeData(t *testing.T, protocol uint) {
peer, _ := newTestPeer("peer", protocol, backend)
defer peer.close()
- // Fetch for now the entire chain db
+ // Collect all state tree hashes.
var hashes []common.Hash
-
it := backend.db.NewIterator(nil, nil)
for it.Next() {
if key := it.Key(); len(key) == common.HashLength {
@@ -449,14 +430,11 @@ func testGetNodeData(t *testing.T, protocol uint) {
}
it.Release()
- if protocol <= ETH65 {
- p2p.Send(peer.app, GetNodeDataMsg, hashes)
- } else {
- p2p.Send(peer.app, GetNodeDataMsg, GetNodeDataPacket66{
- RequestId: 123,
- GetNodeDataPacket: hashes,
- })
- }
+ // Request all hashes.
+ p2p.Send(peer.app, GetNodeDataMsg, GetNodeDataPacket66{
+ RequestId: 123,
+ GetNodeDataPacket: hashes,
+ })
msg, err := peer.app.ReadMsg()
if err != nil {
t.Fatalf("failed to read node data response: %v", err)
@@ -464,49 +442,46 @@ func testGetNodeData(t *testing.T, protocol uint) {
if msg.Code != NodeDataMsg {
t.Fatalf("response packet code mismatch: have %x, want %x", msg.Code, NodeDataMsg)
}
- var data [][]byte
- if protocol <= ETH65 {
- if err := msg.Decode(&data); err != nil {
- t.Fatalf("failed to decode response node data: %v", err)
- }
- } else {
- var res NodeDataPacket66
- if err := msg.Decode(&res); err != nil {
- t.Fatalf("failed to decode response node data: %v", err)
- }
- data = res.NodeDataPacket
+ var res NodeDataPacket66
+ if err := msg.Decode(&res); err != nil {
+ t.Fatalf("failed to decode response node data: %v", err)
}
- // Verify that all hashes correspond to the requested data, and reconstruct a state tree
+
+ // Verify that all hashes correspond to the requested data.
+ data := res.NodeDataPacket
for i, want := range hashes {
if hash := crypto.Keccak256Hash(data[i]); hash != want {
t.Errorf("data hash mismatch: have %x, want %x", hash, want)
}
}
- statedb := rawdb.NewMemoryDatabase()
+
+ // Reconstruct state tree from the received data.
+ reconstructDB := rawdb.NewMemoryDatabase()
for i := 0; i < len(data); i++ {
- statedb.Put(hashes[i].Bytes(), data[i])
+ rawdb.WriteTrieNode(reconstructDB, hashes[i], data[i])
}
+
+ // Sanity check whether all state matches.
accounts := []common.Address{testAddr, acc1Addr, acc2Addr}
for i := uint64(0); i <= backend.chain.CurrentBlock().NumberU64(); i++ {
- trie, _ := state.New(backend.chain.GetBlockByNumber(i).Root(), state.NewDatabase(statedb), nil)
-
+ root := backend.chain.GetBlockByNumber(i).Root()
+ reconstructed, _ := state.New(root, state.NewDatabase(reconstructDB), nil)
for j, acc := range accounts {
- state, _ := backend.chain.State()
+ state, _ := backend.chain.StateAt(root)
bw := state.GetBalance(acc)
- bh := trie.GetBalance(acc)
+ bh := reconstructed.GetBalance(acc)
- if (bw != nil && bh == nil) || (bw == nil && bh != nil) {
- t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
+ if (bw == nil) != (bh == nil) {
+ t.Errorf("block %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
}
- if bw != nil && bh != nil && bw.Cmp(bw) != 0 {
- t.Errorf("test %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
+ if bw != nil && bh != nil && bw.Cmp(bh) != 0 {
+ t.Errorf("block %d, account %d: balance mismatch: have %v, want %v", i, j, bh, bw)
}
}
}
}
// Tests that the transaction receipts can be retrieved based on hashes.
-func TestGetBlockReceipts65(t *testing.T) { testGetBlockReceipts(t, ETH65) }
func TestGetBlockReceipts66(t *testing.T) { testGetBlockReceipts(t, ETH66) }
func testGetBlockReceipts(t *testing.T, protocol uint) {
@@ -566,21 +541,14 @@ func testGetBlockReceipts(t *testing.T, protocol uint) {
receipts = append(receipts, backend.chain.GetReceiptsByHash(block.Hash()))
}
// Send the hash request and verify the response
- if protocol <= ETH65 {
- p2p.Send(peer.app, GetReceiptsMsg, hashes)
- if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, receipts); err != nil {
- t.Errorf("receipts mismatch: %v", err)
- }
- } else {
- p2p.Send(peer.app, GetReceiptsMsg, GetReceiptsPacket66{
- RequestId: 123,
- GetReceiptsPacket: hashes,
- })
- if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, ReceiptsPacket66{
- RequestId: 123,
- ReceiptsPacket: receipts,
- }); err != nil {
- t.Errorf("receipts mismatch: %v", err)
- }
+ p2p.Send(peer.app, GetReceiptsMsg, GetReceiptsPacket66{
+ RequestId: 123,
+ GetReceiptsPacket: hashes,
+ })
+ if err := p2p.ExpectMsg(peer.app, ReceiptsMsg, ReceiptsPacket66{
+ RequestId: 123,
+ ReceiptsPacket: receipts,
+ }); err != nil {
+ t.Errorf("receipts mismatch: %v", err)
}
}
diff --git a/eth/protocols/eth/handlers.go b/eth/protocols/eth/handlers.go
index d7d993a23d..e54838cbc5 100644
--- a/eth/protocols/eth/handlers.go
+++ b/eth/protocols/eth/handlers.go
@@ -27,17 +27,6 @@ import (
"github.com/ethereum/go-ethereum/trie"
)
-// handleGetBlockHeaders handles Block header query, collect the requested headers and reply
-func handleGetBlockHeaders(backend Backend, msg Decoder, peer *Peer) error {
- // Decode the complex header query
- var query GetBlockHeadersPacket
- if err := msg.Decode(&query); err != nil {
- return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
- }
- response := answerGetBlockHeadersQuery(backend, &query, peer)
- return peer.SendBlockHeaders(response)
-}
-
// handleGetBlockHeaders66 is the eth/66 version of handleGetBlockHeaders
func handleGetBlockHeaders66(backend Backend, msg Decoder, peer *Peer) error {
// Decode the complex header query
@@ -135,16 +124,6 @@ func answerGetBlockHeadersQuery(backend Backend, query *GetBlockHeadersPacket, p
return headers
}
-func handleGetBlockBodies(backend Backend, msg Decoder, peer *Peer) error {
- // Decode the block body retrieval message
- var query GetBlockBodiesPacket
- if err := msg.Decode(&query); err != nil {
- return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
- }
- response := answerGetBlockBodiesQuery(backend, query, peer)
- return peer.SendBlockBodiesRLP(response)
-}
-
func handleGetBlockBodies66(backend Backend, msg Decoder, peer *Peer) error {
// Decode the block body retrieval message
var query GetBlockBodiesPacket66
@@ -174,16 +153,6 @@ func answerGetBlockBodiesQuery(backend Backend, query GetBlockBodiesPacket, peer
return bodies
}
-func handleGetNodeData(backend Backend, msg Decoder, peer *Peer) error {
- // Decode the trie node data retrieval message
- var query GetNodeDataPacket
- if err := msg.Decode(&query); err != nil {
- return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
- }
- response := answerGetNodeDataQuery(backend, query, peer)
- return peer.SendNodeData(response)
-}
-
func handleGetNodeData66(backend Backend, msg Decoder, peer *Peer) error {
// Decode the trie node data retrieval message
var query GetNodeDataPacket66
@@ -223,16 +192,6 @@ func answerGetNodeDataQuery(backend Backend, query GetNodeDataPacket, peer *Peer
return nodes
}
-func handleGetReceipts(backend Backend, msg Decoder, peer *Peer) error {
- // Decode the block receipts retrieval message
- var query GetReceiptsPacket
- if err := msg.Decode(&query); err != nil {
- return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
- }
- response := answerGetReceiptsQuery(backend, query, peer)
- return peer.SendReceiptsRLP(response)
-}
-
func handleGetReceipts66(backend Backend, msg Decoder, peer *Peer) error {
// Decode the block receipts retrieval message
var query GetReceiptsPacket66
@@ -312,15 +271,6 @@ func handleNewBlock(backend Backend, msg Decoder, peer *Peer) error {
return backend.Handle(peer, ann)
}
-func handleBlockHeaders(backend Backend, msg Decoder, peer *Peer) error {
- // A batch of headers arrived to one of our previous requests
- res := new(BlockHeadersPacket)
- if err := msg.Decode(res); err != nil {
- return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
- }
- return backend.Handle(peer, res)
-}
-
func handleBlockHeaders66(backend Backend, msg Decoder, peer *Peer) error {
// A batch of headers arrived to one of our previous requests
res := new(BlockHeadersPacket66)
@@ -332,15 +282,6 @@ func handleBlockHeaders66(backend Backend, msg Decoder, peer *Peer) error {
return backend.Handle(peer, &res.BlockHeadersPacket)
}
-func handleBlockBodies(backend Backend, msg Decoder, peer *Peer) error {
- // A batch of block bodies arrived to one of our previous requests
- res := new(BlockBodiesPacket)
- if err := msg.Decode(res); err != nil {
- return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
- }
- return backend.Handle(peer, res)
-}
-
func handleBlockBodies66(backend Backend, msg Decoder, peer *Peer) error {
// A batch of block bodies arrived to one of our previous requests
res := new(BlockBodiesPacket66)
@@ -352,15 +293,6 @@ func handleBlockBodies66(backend Backend, msg Decoder, peer *Peer) error {
return backend.Handle(peer, &res.BlockBodiesPacket)
}
-func handleNodeData(backend Backend, msg Decoder, peer *Peer) error {
- // A batch of node state data arrived to one of our previous requests
- res := new(NodeDataPacket)
- if err := msg.Decode(res); err != nil {
- return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
- }
- return backend.Handle(peer, res)
-}
-
func handleNodeData66(backend Backend, msg Decoder, peer *Peer) error {
// A batch of node state data arrived to one of our previous requests
res := new(NodeDataPacket66)
@@ -372,15 +304,6 @@ func handleNodeData66(backend Backend, msg Decoder, peer *Peer) error {
return backend.Handle(peer, &res.NodeDataPacket)
}
-func handleReceipts(backend Backend, msg Decoder, peer *Peer) error {
- // A batch of receipts arrived to one of our previous requests
- res := new(ReceiptsPacket)
- if err := msg.Decode(res); err != nil {
- return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
- }
- return backend.Handle(peer, res)
-}
-
func handleReceipts66(backend Backend, msg Decoder, peer *Peer) error {
// A batch of receipts arrived to one of our previous requests
res := new(ReceiptsPacket66)
@@ -409,16 +332,6 @@ func handleNewPooledTransactionHashes(backend Backend, msg Decoder, peer *Peer)
return backend.Handle(peer, ann)
}
-func handleGetPooledTransactions(backend Backend, msg Decoder, peer *Peer) error {
- // Decode the pooled transactions retrieval message
- var query GetPooledTransactionsPacket
- if err := msg.Decode(&query); err != nil {
- return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
- }
- hashes, txs := answerGetPooledTransactions(backend, query, peer)
- return peer.SendPooledTransactionsRLP(hashes, txs)
-}
-
func handleGetPooledTransactions66(backend Backend, msg Decoder, peer *Peer) error {
// Decode the pooled transactions retrieval message
var query GetPooledTransactionsPacket66
@@ -477,26 +390,6 @@ func handleTransactions(backend Backend, msg Decoder, peer *Peer) error {
return backend.Handle(peer, &txs)
}
-func handlePooledTransactions(backend Backend, msg Decoder, peer *Peer) error {
- // Transactions arrived, make sure we have a valid and fresh chain to handle them
- if !backend.AcceptTxs() {
- return nil
- }
- // Transactions can be processed, parse all of them and deliver to the pool
- var txs PooledTransactionsPacket
- if err := msg.Decode(&txs); err != nil {
- return fmt.Errorf("%w: message %v: %v", errDecode, msg, err)
- }
- for i, tx := range txs {
- // Validate and mark the remote transaction
- if tx == nil {
- return fmt.Errorf("%w: transaction %d is nil", errDecode, i)
- }
- peer.markTransaction(tx.Hash())
- }
- return backend.Handle(peer, &txs)
-}
-
func handlePooledTransactions66(backend Backend, msg Decoder, peer *Peer) error {
// Transactions arrived, make sure we have a valid and fresh chain to handle them
if !backend.AcceptTxs() {
diff --git a/eth/protocols/eth/handshake_test.go b/eth/protocols/eth/handshake_test.go
index 3bebda2dcc..05d473e057 100644
--- a/eth/protocols/eth/handshake_test.go
+++ b/eth/protocols/eth/handshake_test.go
@@ -27,7 +27,6 @@ import (
)
// Tests that handshake failures are detected and reported correctly.
-func TestHandshake65(t *testing.T) { testHandshake(t, ETH65) }
func TestHandshake66(t *testing.T) { testHandshake(t, ETH66) }
func testHandshake(t *testing.T, protocol uint) {
diff --git a/eth/protocols/eth/peer.go b/eth/protocols/eth/peer.go
index e619c183ba..1b4cfeb3da 100644
--- a/eth/protocols/eth/peer.go
+++ b/eth/protocols/eth/peer.go
@@ -75,12 +75,12 @@ type Peer struct {
head common.Hash // Latest advertised head block hash
td *big.Int // Latest advertised head block total difficulty
- knownBlocks mapset.Set // Set of block hashes known to be known by this peer
+ knownBlocks *knownCache // Set of block hashes known to be known by this peer
queuedBlocks chan *blockPropagation // Queue of blocks to broadcast to the peer
queuedBlockAnns chan *types.Block // Queue of blocks to announce to the peer
txpool TxPool // Transaction pool used by the broadcasters for liveness checks
- knownTxs mapset.Set // Set of transaction hashes known to be known by this peer
+ knownTxs *knownCache // Set of transaction hashes known to be known by this peer
txBroadcast chan []common.Hash // Channel used to queue transaction propagation requests
txAnnounce chan []common.Hash // Channel used to queue transaction announcement requests
@@ -96,8 +96,8 @@ func NewPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter, txpool TxPool) *Pe
Peer: p,
rw: rw,
version: version,
- knownTxs: mapset.NewSet(),
- knownBlocks: mapset.NewSet(),
+ knownTxs: newKnownCache(maxKnownTxs),
+ knownBlocks: newKnownCache(maxKnownBlocks),
queuedBlocks: make(chan *blockPropagation, maxQueuedBlocks),
queuedBlockAnns: make(chan *types.Block, maxQueuedBlockAnns),
txBroadcast: make(chan []common.Hash),
@@ -108,9 +108,8 @@ func NewPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter, txpool TxPool) *Pe
// Start up all the broadcasters
go peer.broadcastBlocks()
go peer.broadcastTransactions()
- if version >= ETH65 {
- go peer.announceTransactions()
- }
+ go peer.announceTransactions()
+
return peer
}
@@ -163,9 +162,6 @@ func (p *Peer) KnownTransaction(hash common.Hash) bool {
// never be propagated to this particular peer.
func (p *Peer) markBlock(hash common.Hash) {
// If we reached the memory allowance, drop a previously known block hash
- for p.knownBlocks.Cardinality() >= maxKnownBlocks {
- p.knownBlocks.Pop()
- }
p.knownBlocks.Add(hash)
}
@@ -173,9 +169,6 @@ func (p *Peer) markBlock(hash common.Hash) {
// will never be propagated to this particular peer.
func (p *Peer) markTransaction(hash common.Hash) {
// If we reached the memory allowance, drop a previously known transaction hash
- for p.knownTxs.Cardinality() >= maxKnownTxs {
- p.knownTxs.Pop()
- }
p.knownTxs.Add(hash)
}
@@ -190,9 +183,6 @@ func (p *Peer) markTransaction(hash common.Hash) {
// tests that directly send messages without having to do the asyn queueing.
func (p *Peer) SendTransactions(txs types.Transactions) error {
// Mark all the transactions as known, but ensure we don't overflow our limits
- for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(txs)) {
- p.knownTxs.Pop()
- }
for _, tx := range txs {
p.knownTxs.Add(tx.Hash())
}
@@ -206,12 +196,7 @@ func (p *Peer) AsyncSendTransactions(hashes []common.Hash) {
select {
case p.txBroadcast <- hashes:
// Mark all the transactions as known, but ensure we don't overflow our limits
- for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) {
- p.knownTxs.Pop()
- }
- for _, hash := range hashes {
- p.knownTxs.Add(hash)
- }
+ p.knownTxs.Add(hashes...)
case <-p.term:
p.Log().Debug("Dropping transaction propagation", "count", len(hashes))
}
@@ -225,12 +210,7 @@ func (p *Peer) AsyncSendTransactions(hashes []common.Hash) {
// not be managed directly.
func (p *Peer) sendPooledTransactionHashes(hashes []common.Hash) error {
// Mark all the transactions as known, but ensure we don't overflow our limits
- for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) {
- p.knownTxs.Pop()
- }
- for _, hash := range hashes {
- p.knownTxs.Add(hash)
- }
+ p.knownTxs.Add(hashes...)
return p2p.Send(p.rw, NewPooledTransactionHashesMsg, NewPooledTransactionHashesPacket(hashes))
}
@@ -241,42 +221,17 @@ func (p *Peer) AsyncSendPooledTransactionHashes(hashes []common.Hash) {
select {
case p.txAnnounce <- hashes:
// Mark all the transactions as known, but ensure we don't overflow our limits
- for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) {
- p.knownTxs.Pop()
- }
- for _, hash := range hashes {
- p.knownTxs.Add(hash)
- }
+ p.knownTxs.Add(hashes...)
case <-p.term:
p.Log().Debug("Dropping transaction announcement", "count", len(hashes))
}
}
-// SendPooledTransactionsRLP sends requested transactions to the peer and adds the
-// hashes in its transaction hash set for future reference.
-//
-// Note, the method assumes the hashes are correct and correspond to the list of
-// transactions being sent.
-func (p *Peer) SendPooledTransactionsRLP(hashes []common.Hash, txs []rlp.RawValue) error {
- // Mark all the transactions as known, but ensure we don't overflow our limits
- for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) {
- p.knownTxs.Pop()
- }
- for _, hash := range hashes {
- p.knownTxs.Add(hash)
- }
- return p2p.Send(p.rw, PooledTransactionsMsg, txs) // Not packed into PooledTransactionsPacket to avoid RLP decoding
-}
-
// ReplyPooledTransactionsRLP is the eth/66 version of SendPooledTransactionsRLP.
func (p *Peer) ReplyPooledTransactionsRLP(id uint64, hashes []common.Hash, txs []rlp.RawValue) error {
// Mark all the transactions as known, but ensure we don't overflow our limits
- for p.knownTxs.Cardinality() > max(0, maxKnownTxs-len(hashes)) {
- p.knownTxs.Pop()
- }
- for _, hash := range hashes {
- p.knownTxs.Add(hash)
- }
+ p.knownTxs.Add(hashes...)
+
// Not packed into PooledTransactionsPacket to avoid RLP decoding
return p2p.Send(p.rw, PooledTransactionsMsg, PooledTransactionsRLPPacket66{
RequestId: id,
@@ -288,12 +243,8 @@ func (p *Peer) ReplyPooledTransactionsRLP(id uint64, hashes []common.Hash, txs [
// a hash notification.
func (p *Peer) SendNewBlockHashes(hashes []common.Hash, numbers []uint64) error {
// Mark all the block hashes as known, but ensure we don't overflow our limits
- for p.knownBlocks.Cardinality() > max(0, maxKnownBlocks-len(hashes)) {
- p.knownBlocks.Pop()
- }
- for _, hash := range hashes {
- p.knownBlocks.Add(hash)
- }
+ p.knownBlocks.Add(hashes...)
+
request := make(NewBlockHashesPacket, len(hashes))
for i := 0; i < len(hashes); i++ {
request[i].Hash = hashes[i]
@@ -309,9 +260,6 @@ func (p *Peer) AsyncSendNewBlockHash(block *types.Block) {
select {
case p.queuedBlockAnns <- block:
// Mark all the block hash as known, but ensure we don't overflow our limits
- for p.knownBlocks.Cardinality() >= maxKnownBlocks {
- p.knownBlocks.Pop()
- }
p.knownBlocks.Add(block.Hash())
default:
p.Log().Debug("Dropping block announcement", "number", block.NumberU64(), "hash", block.Hash())
@@ -321,9 +269,6 @@ func (p *Peer) AsyncSendNewBlockHash(block *types.Block) {
// SendNewBlock propagates an entire block to a remote peer.
func (p *Peer) SendNewBlock(block *types.Block, td *big.Int) error {
// Mark all the block hash as known, but ensure we don't overflow our limits
- for p.knownBlocks.Cardinality() >= maxKnownBlocks {
- p.knownBlocks.Pop()
- }
p.knownBlocks.Add(block.Hash())
return p2p.Send(p.rw, NewBlockMsg, &NewBlockPacket{
Block: block,
@@ -337,20 +282,12 @@ func (p *Peer) AsyncSendNewBlock(block *types.Block, td *big.Int) {
select {
case p.queuedBlocks <- &blockPropagation{block: block, td: td}:
// Mark all the block hash as known, but ensure we don't overflow our limits
- for p.knownBlocks.Cardinality() >= maxKnownBlocks {
- p.knownBlocks.Pop()
- }
p.knownBlocks.Add(block.Hash())
default:
p.Log().Debug("Dropping block propagation", "number", block.NumberU64(), "hash", block.Hash())
}
}
-// SendBlockHeaders sends a batch of block headers to the remote peer.
-func (p *Peer) SendBlockHeaders(headers []*types.Header) error {
- return p2p.Send(p.rw, BlockHeadersMsg, BlockHeadersPacket(headers))
-}
-
// ReplyBlockHeaders is the eth/66 version of SendBlockHeaders.
func (p *Peer) ReplyBlockHeaders(id uint64, headers []*types.Header) error {
return p2p.Send(p.rw, BlockHeadersMsg, BlockHeadersPacket66{
@@ -359,12 +296,6 @@ func (p *Peer) ReplyBlockHeaders(id uint64, headers []*types.Header) error {
})
}
-// SendBlockBodiesRLP sends a batch of block contents to the remote peer from
-// an already RLP encoded format.
-func (p *Peer) SendBlockBodiesRLP(bodies []rlp.RawValue) error {
- return p2p.Send(p.rw, BlockBodiesMsg, bodies) // Not packed into BlockBodiesPacket to avoid RLP decoding
-}
-
// ReplyBlockBodiesRLP is the eth/66 version of SendBlockBodiesRLP.
func (p *Peer) ReplyBlockBodiesRLP(id uint64, bodies []rlp.RawValue) error {
// Not packed into BlockBodiesPacket to avoid RLP decoding
@@ -374,12 +305,6 @@ func (p *Peer) ReplyBlockBodiesRLP(id uint64, bodies []rlp.RawValue) error {
})
}
-// SendNodeDataRLP sends a batch of arbitrary internal data, corresponding to the
-// hashes requested.
-func (p *Peer) SendNodeData(data [][]byte) error {
- return p2p.Send(p.rw, NodeDataMsg, NodeDataPacket(data))
-}
-
// ReplyNodeData is the eth/66 response to GetNodeData.
func (p *Peer) ReplyNodeData(id uint64, data [][]byte) error {
return p2p.Send(p.rw, NodeDataMsg, NodeDataPacket66{
@@ -388,12 +313,6 @@ func (p *Peer) ReplyNodeData(id uint64, data [][]byte) error {
})
}
-// SendReceiptsRLP sends a batch of transaction receipts, corresponding to the
-// ones requested from an already RLP encoded format.
-func (p *Peer) SendReceiptsRLP(receipts []rlp.RawValue) error {
- return p2p.Send(p.rw, ReceiptsMsg, receipts) // Not packed into ReceiptsPacket to avoid RLP decoding
-}
-
// ReplyReceiptsRLP is the eth/66 response to GetReceipts.
func (p *Peer) ReplyReceiptsRLP(id uint64, receipts []rlp.RawValue) error {
return p2p.Send(p.rw, ReceiptsMsg, ReceiptsRLPPacket66{
@@ -406,138 +325,136 @@ func (p *Peer) ReplyReceiptsRLP(id uint64, receipts []rlp.RawValue) error {
// single header. It is used solely by the fetcher.
func (p *Peer) RequestOneHeader(hash common.Hash) error {
p.Log().Debug("Fetching single header", "hash", hash)
- query := GetBlockHeadersPacket{
- Origin: HashOrNumber{Hash: hash},
- Amount: uint64(1),
- Skip: uint64(0),
- Reverse: false,
- }
- if p.Version() >= ETH66 {
- id := rand.Uint64()
+ id := rand.Uint64()
- requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id)
- return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{
- RequestId: id,
- GetBlockHeadersPacket: &query,
- })
- }
- return p2p.Send(p.rw, GetBlockHeadersMsg, &query)
+ requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id)
+ return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{
+ RequestId: id,
+ GetBlockHeadersPacket: &GetBlockHeadersPacket{
+ Origin: HashOrNumber{Hash: hash},
+ Amount: uint64(1),
+ Skip: uint64(0),
+ Reverse: false,
+ },
+ })
}
// RequestHeadersByHash fetches a batch of blocks' headers corresponding to the
// specified header query, based on the hash of an origin block.
func (p *Peer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error {
p.Log().Debug("Fetching batch of headers", "count", amount, "fromhash", origin, "skip", skip, "reverse", reverse)
- query := GetBlockHeadersPacket{
- Origin: HashOrNumber{Hash: origin},
- Amount: uint64(amount),
- Skip: uint64(skip),
- Reverse: reverse,
- }
- if p.Version() >= ETH66 {
- id := rand.Uint64()
+ id := rand.Uint64()
- requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id)
- return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{
- RequestId: id,
- GetBlockHeadersPacket: &query,
- })
- }
- return p2p.Send(p.rw, GetBlockHeadersMsg, &query)
+ requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id)
+ return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{
+ RequestId: id,
+ GetBlockHeadersPacket: &GetBlockHeadersPacket{
+ Origin: HashOrNumber{Hash: origin},
+ Amount: uint64(amount),
+ Skip: uint64(skip),
+ Reverse: reverse,
+ },
+ })
}
// RequestHeadersByNumber fetches a batch of blocks' headers corresponding to the
// specified header query, based on the number of an origin block.
func (p *Peer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error {
p.Log().Debug("Fetching batch of headers", "count", amount, "fromnum", origin, "skip", skip, "reverse", reverse)
- query := GetBlockHeadersPacket{
- Origin: HashOrNumber{Number: origin},
- Amount: uint64(amount),
- Skip: uint64(skip),
- Reverse: reverse,
- }
- if p.Version() >= ETH66 {
- id := rand.Uint64()
+ id := rand.Uint64()
- requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id)
- return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{
- RequestId: id,
- GetBlockHeadersPacket: &query,
- })
- }
- return p2p.Send(p.rw, GetBlockHeadersMsg, &query)
-}
-
-// ExpectRequestHeadersByNumber is a testing method to mirror the recipient side
-// of the RequestHeadersByNumber operation.
-func (p *Peer) ExpectRequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error {
- req := &GetBlockHeadersPacket{
- Origin: HashOrNumber{Number: origin},
- Amount: uint64(amount),
- Skip: uint64(skip),
- Reverse: reverse,
- }
- return p2p.ExpectMsg(p.rw, GetBlockHeadersMsg, req)
+ requestTracker.Track(p.id, p.version, GetBlockHeadersMsg, BlockHeadersMsg, id)
+ return p2p.Send(p.rw, GetBlockHeadersMsg, &GetBlockHeadersPacket66{
+ RequestId: id,
+ GetBlockHeadersPacket: &GetBlockHeadersPacket{
+ Origin: HashOrNumber{Number: origin},
+ Amount: uint64(amount),
+ Skip: uint64(skip),
+ Reverse: reverse,
+ },
+ })
}
// RequestBodies fetches a batch of blocks' bodies corresponding to the hashes
// specified.
func (p *Peer) RequestBodies(hashes []common.Hash) error {
p.Log().Debug("Fetching batch of block bodies", "count", len(hashes))
- if p.Version() >= ETH66 {
- id := rand.Uint64()
+ id := rand.Uint64()
- requestTracker.Track(p.id, p.version, GetBlockBodiesMsg, BlockBodiesMsg, id)
- return p2p.Send(p.rw, GetBlockBodiesMsg, &GetBlockBodiesPacket66{
- RequestId: id,
- GetBlockBodiesPacket: hashes,
- })
- }
- return p2p.Send(p.rw, GetBlockBodiesMsg, GetBlockBodiesPacket(hashes))
+ requestTracker.Track(p.id, p.version, GetBlockBodiesMsg, BlockBodiesMsg, id)
+ return p2p.Send(p.rw, GetBlockBodiesMsg, &GetBlockBodiesPacket66{
+ RequestId: id,
+ GetBlockBodiesPacket: hashes,
+ })
}
// RequestNodeData fetches a batch of arbitrary data from a node's known state
// data, corresponding to the specified hashes.
func (p *Peer) RequestNodeData(hashes []common.Hash) error {
p.Log().Debug("Fetching batch of state data", "count", len(hashes))
- if p.Version() >= ETH66 {
- id := rand.Uint64()
+ id := rand.Uint64()
- requestTracker.Track(p.id, p.version, GetNodeDataMsg, NodeDataMsg, id)
- return p2p.Send(p.rw, GetNodeDataMsg, &GetNodeDataPacket66{
- RequestId: id,
- GetNodeDataPacket: hashes,
- })
- }
- return p2p.Send(p.rw, GetNodeDataMsg, GetNodeDataPacket(hashes))
+ requestTracker.Track(p.id, p.version, GetNodeDataMsg, NodeDataMsg, id)
+ return p2p.Send(p.rw, GetNodeDataMsg, &GetNodeDataPacket66{
+ RequestId: id,
+ GetNodeDataPacket: hashes,
+ })
}
// RequestReceipts fetches a batch of transaction receipts from a remote node.
func (p *Peer) RequestReceipts(hashes []common.Hash) error {
p.Log().Debug("Fetching batch of receipts", "count", len(hashes))
- if p.Version() >= ETH66 {
- id := rand.Uint64()
+ id := rand.Uint64()
- requestTracker.Track(p.id, p.version, GetReceiptsMsg, ReceiptsMsg, id)
- return p2p.Send(p.rw, GetReceiptsMsg, &GetReceiptsPacket66{
- RequestId: id,
- GetReceiptsPacket: hashes,
- })
- }
- return p2p.Send(p.rw, GetReceiptsMsg, GetReceiptsPacket(hashes))
+ requestTracker.Track(p.id, p.version, GetReceiptsMsg, ReceiptsMsg, id)
+ return p2p.Send(p.rw, GetReceiptsMsg, &GetReceiptsPacket66{
+ RequestId: id,
+ GetReceiptsPacket: hashes,
+ })
}
// RequestTxs fetches a batch of transactions from a remote node.
func (p *Peer) RequestTxs(hashes []common.Hash) error {
p.Log().Debug("Fetching batch of transactions", "count", len(hashes))
- if p.Version() >= ETH66 {
- id := rand.Uint64()
+ id := rand.Uint64()
- requestTracker.Track(p.id, p.version, GetPooledTransactionsMsg, PooledTransactionsMsg, id)
- return p2p.Send(p.rw, GetPooledTransactionsMsg, &GetPooledTransactionsPacket66{
- RequestId: id,
- GetPooledTransactionsPacket: hashes,
- })
- }
- return p2p.Send(p.rw, GetPooledTransactionsMsg, GetPooledTransactionsPacket(hashes))
+ requestTracker.Track(p.id, p.version, GetPooledTransactionsMsg, PooledTransactionsMsg, id)
+ return p2p.Send(p.rw, GetPooledTransactionsMsg, &GetPooledTransactionsPacket66{
+ RequestId: id,
+ GetPooledTransactionsPacket: hashes,
+ })
+}
+
+// knownCache is a cache for known hashes.
+type knownCache struct {
+ hashes mapset.Set
+ max int
+}
+
+// newKnownCache creates a new knownCache with a max capacity.
+func newKnownCache(max int) *knownCache {
+ return &knownCache{
+ max: max,
+ hashes: mapset.NewSet(),
+ }
+}
+
+// Add adds a list of elements to the set.
+func (k *knownCache) Add(hashes ...common.Hash) {
+ for k.hashes.Cardinality() > max(0, k.max-len(hashes)) {
+ k.hashes.Pop()
+ }
+ for _, hash := range hashes {
+ k.hashes.Add(hash)
+ }
+}
+
+// Contains returns whether the given item is in the set.
+func (k *knownCache) Contains(hash common.Hash) bool {
+ return k.hashes.Contains(hash)
+}
+
+// Cardinality returns the number of elements in the set.
+func (k *knownCache) Cardinality() int {
+ return k.hashes.Cardinality()
}
diff --git a/eth/protocols/eth/peer_test.go b/eth/protocols/eth/peer_test.go
index 70e9959f82..fc93443708 100644
--- a/eth/protocols/eth/peer_test.go
+++ b/eth/protocols/eth/peer_test.go
@@ -21,7 +21,9 @@ package eth
import (
"crypto/rand"
+ "testing"
+ "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/p2p/enode"
)
@@ -59,3 +61,28 @@ func (p *testPeer) close() {
p.Peer.Close()
p.app.Close()
}
+
+func TestPeerSet(t *testing.T) {
+ size := 5
+ s := newKnownCache(size)
+
+ // add 10 items
+ for i := 0; i < size*2; i++ {
+ s.Add(common.Hash{byte(i)})
+ }
+
+ if s.Cardinality() != size {
+ t.Fatalf("wrong size, expected %d but found %d", size, s.Cardinality())
+ }
+
+ vals := []common.Hash{}
+ for i := 10; i < 20; i++ {
+ vals = append(vals, common.Hash{byte(i)})
+ }
+
+ // add item in batch
+ s.Add(vals...)
+ if s.Cardinality() < size {
+ t.Fatalf("bad size")
+ }
+}
diff --git a/eth/protocols/eth/protocol.go b/eth/protocols/eth/protocol.go
index de1b0ed1ee..3c3da30fa5 100644
--- a/eth/protocols/eth/protocol.go
+++ b/eth/protocols/eth/protocol.go
@@ -30,7 +30,6 @@ import (
// Constants to match up protocol versions and messages
const (
- ETH65 = 65
ETH66 = 66
)
@@ -40,31 +39,28 @@ const ProtocolName = "eth"
// ProtocolVersions are the supported versions of the `eth` protocol (first
// is primary).
-var ProtocolVersions = []uint{ETH66, ETH65}
+var ProtocolVersions = []uint{ETH66}
// protocolLengths are the number of implemented message corresponding to
// different protocol versions.
-var protocolLengths = map[uint]uint64{ETH66: 17, ETH65: 17}
+var protocolLengths = map[uint]uint64{ETH66: 17}
// maxMessageSize is the maximum cap on the size of a protocol message.
const maxMessageSize = 10 * 1024 * 1024
const (
- // Protocol messages in eth/64
- StatusMsg = 0x00
- NewBlockHashesMsg = 0x01
- TransactionsMsg = 0x02
- GetBlockHeadersMsg = 0x03
- BlockHeadersMsg = 0x04
- GetBlockBodiesMsg = 0x05
- BlockBodiesMsg = 0x06
- NewBlockMsg = 0x07
- GetNodeDataMsg = 0x0d
- NodeDataMsg = 0x0e
- GetReceiptsMsg = 0x0f
- ReceiptsMsg = 0x10
-
- // Protocol messages overloaded in eth/65
+ StatusMsg = 0x00
+ NewBlockHashesMsg = 0x01
+ TransactionsMsg = 0x02
+ GetBlockHeadersMsg = 0x03
+ BlockHeadersMsg = 0x04
+ GetBlockBodiesMsg = 0x05
+ BlockBodiesMsg = 0x06
+ NewBlockMsg = 0x07
+ GetNodeDataMsg = 0x0d
+ NodeDataMsg = 0x0e
+ GetReceiptsMsg = 0x0f
+ ReceiptsMsg = 0x10
NewPooledTransactionHashesMsg = 0x08
GetPooledTransactionsMsg = 0x09
PooledTransactionsMsg = 0x0a
@@ -128,7 +124,7 @@ type GetBlockHeadersPacket struct {
Reverse bool // Query direction (false = rising towards latest, true = falling towards genesis)
}
-// GetBlockHeadersPacket represents a block header query over eth/66
+// GetBlockHeadersPacket66 represents a block header query over eth/66
type GetBlockHeadersPacket66 struct {
RequestId uint64
*GetBlockHeadersPacket
diff --git a/eth/protocols/snap/handler.go b/eth/protocols/snap/handler.go
index 3d668a2ebb..c62f9cfca5 100644
--- a/eth/protocols/snap/handler.go
+++ b/eth/protocols/snap/handler.go
@@ -23,7 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core"
- "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/metrics"
@@ -319,7 +319,7 @@ func handleMessage(backend Backend, peer *Peer) error {
if err != nil {
return p2p.Send(peer.rw, StorageRangesMsg, &StorageRangesPacket{ID: req.ID})
}
- var acc state.Account
+ var acc types.StateAccount
if err := rlp.DecodeBytes(accTrie.Get(account[:]), &acc); err != nil {
return p2p.Send(peer.rw, StorageRangesMsg, &StorageRangesPacket{ID: req.ID})
}
@@ -469,7 +469,7 @@ func handleMessage(backend Backend, peer *Peer) error {
// Storage slots requested, open the storage trie and retrieve from there
account, err := snap.Account(common.BytesToHash(pathset[0]))
loads++ // always account database reads, even for failures
- if err != nil {
+ if err != nil || account == nil {
break
}
stTrie, err := trie.NewSecure(common.BytesToHash(account.Root), triedb)
diff --git a/eth/protocols/snap/sync.go b/eth/protocols/snap/sync.go
index 646df03887..9ef9d75711 100644
--- a/eth/protocols/snap/sync.go
+++ b/eth/protocols/snap/sync.go
@@ -32,6 +32,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/state/snapshot"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
@@ -125,8 +126,8 @@ type accountRequest struct {
type accountResponse struct {
task *accountTask // Task which this request is filling
- hashes []common.Hash // Account hashes in the returned range
- accounts []*state.Account // Expanded accounts in the returned range
+ hashes []common.Hash // Account hashes in the returned range
+ accounts []*types.StateAccount // Expanded accounts in the returned range
cont bool // Whether the account range has a continuation
}
@@ -2274,9 +2275,9 @@ func (s *Syncer) OnAccounts(peer SyncPeer, id uint64, hashes []common.Hash, acco
s.scheduleRevertAccountRequest(req)
return err
}
- accs := make([]*state.Account, len(accounts))
+ accs := make([]*types.StateAccount, len(accounts))
for i, account := range accounts {
- acc := new(state.Account)
+ acc := new(types.StateAccount)
if err := rlp.DecodeBytes(account, acc); err != nil {
panic(err) // We created these blobs, we must be able to decode them
}
@@ -2740,7 +2741,7 @@ func (s *Syncer) onHealByteCodes(peer SyncPeer, id uint64, bytecodes [][]byte) e
// Note it's not concurrent safe, please handle the concurrent issue outside.
func (s *Syncer) onHealState(paths [][]byte, value []byte) error {
if len(paths) == 1 {
- var account state.Account
+ var account types.StateAccount
if err := rlp.DecodeBytes(value, &account); err != nil {
return nil
}
diff --git a/eth/protocols/snap/sync_test.go b/eth/protocols/snap/sync_test.go
index 023fc8ee00..47ab1f026d 100644
--- a/eth/protocols/snap/sync_test.go
+++ b/eth/protocols/snap/sync_test.go
@@ -29,7 +29,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/light"
@@ -1349,7 +1349,7 @@ func makeAccountTrieNoStorage(n int) (*trie.Trie, entrySlice) {
accTrie, _ := trie.New(common.Hash{}, db)
var entries entrySlice
for i := uint64(1); i <= uint64(n); i++ {
- value, _ := rlp.EncodeToBytes(state.Account{
+ value, _ := rlp.EncodeToBytes(types.StateAccount{
Nonce: i,
Balance: big.NewInt(int64(i)),
Root: emptyRoot,
@@ -1394,7 +1394,7 @@ func makeBoundaryAccountTrie(n int) (*trie.Trie, entrySlice) {
}
// Fill boundary accounts
for i := 0; i < len(boundaries); i++ {
- value, _ := rlp.EncodeToBytes(state.Account{
+ value, _ := rlp.EncodeToBytes(types.StateAccount{
Nonce: uint64(0),
Balance: big.NewInt(int64(i)),
Root: emptyRoot,
@@ -1406,7 +1406,7 @@ func makeBoundaryAccountTrie(n int) (*trie.Trie, entrySlice) {
}
// Fill other accounts if required
for i := uint64(1); i <= uint64(n); i++ {
- value, _ := rlp.EncodeToBytes(state.Account{
+ value, _ := rlp.EncodeToBytes(types.StateAccount{
Nonce: i,
Balance: big.NewInt(int64(i)),
Root: emptyRoot,
@@ -1442,7 +1442,7 @@ func makeAccountTrieWithStorageWithUniqueStorage(accounts, slots int, code bool)
stTrie, stEntries := makeStorageTrieWithSeed(uint64(slots), i, db)
stRoot := stTrie.Hash()
stTrie.Commit(nil)
- value, _ := rlp.EncodeToBytes(state.Account{
+ value, _ := rlp.EncodeToBytes(types.StateAccount{
Nonce: i,
Balance: big.NewInt(int64(i)),
Root: stRoot,
@@ -1489,7 +1489,7 @@ func makeAccountTrieWithStorage(accounts, slots int, code, boundary bool) (*trie
if code {
codehash = getCodeHash(i)
}
- value, _ := rlp.EncodeToBytes(state.Account{
+ value, _ := rlp.EncodeToBytes(types.StateAccount{
Nonce: i,
Balance: big.NewInt(int64(i)),
Root: stRoot,
diff --git a/eth/state_accessor.go b/eth/state_accessor.go
index eb178311f3..ca2002b60d 100644
--- a/eth/state_accessor.go
+++ b/eth/state_accessor.go
@@ -119,7 +119,8 @@ func (eth *Ethereum) stateAtBlock(block *types.Block, reexec uint64, base *state
// Finalize the state so any modifications are written to the trie
root, err := statedb.Commit(eth.blockchain.Config().IsEIP158(current.Number()))
if err != nil {
- return nil, err
+ return nil, fmt.Errorf("stateAtBlock commit failed, number %d root %v: %w",
+ current.NumberU64(), current.Root().Hex(), err)
}
statedb, err = state.New(root, database, nil)
if err != nil {
diff --git a/eth/sync.go b/eth/sync.go
index ab114b59f3..aaac6bef90 100644
--- a/eth/sync.go
+++ b/eth/sync.go
@@ -18,7 +18,6 @@ package eth
import (
"math/big"
- "math/rand"
"sync/atomic"
"time"
@@ -28,23 +27,13 @@ import (
"github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/log"
- "github.com/ethereum/go-ethereum/p2p/enode"
)
const (
forceSyncCycle = 10 * time.Second // Time interval to force syncs, even if few peers are available
defaultMinSyncPeers = 5 // Amount of peers desired to start syncing
-
- // This is the target size for the packs of transactions sent by txsyncLoop64.
- // A pack can get larger than this if a single transactions exceeds this size.
- txsyncPackSize = 100 * 1024
)
-type txsync struct {
- p *eth.Peer
- txs []*types.Transaction
-}
-
// syncTransactions starts sending all currently pending transactions to the given peer.
func (h *handler) syncTransactions(p *eth.Peer) {
// Assemble the set of transaction to broadcast or announce to the remote
@@ -54,7 +43,7 @@ func (h *handler) syncTransactions(p *eth.Peer) {
//
// TODO(karalabe): Figure out if we could get away with random order somehow
var txs types.Transactions
- pending, _ := h.txpool.Pending(false)
+ pending := h.txpool.Pending(false)
for _, batch := range pending {
txs = append(txs, batch...)
}
@@ -64,94 +53,11 @@ func (h *handler) syncTransactions(p *eth.Peer) {
// The eth/65 protocol introduces proper transaction announcements, so instead
// of dripping transactions across multiple peers, just send the entire list as
// an announcement and let the remote side decide what they need (likely nothing).
- if p.Version() >= eth.ETH65 {
- hashes := make([]common.Hash, len(txs))
- for i, tx := range txs {
- hashes[i] = tx.Hash()
- }
- p.AsyncSendPooledTransactionHashes(hashes)
- return
- }
- // Out of luck, peer is running legacy protocols, drop the txs over
- select {
- case h.txsyncCh <- &txsync{p: p, txs: txs}:
- case <-h.quitSync:
- }
-}
-
-// txsyncLoop64 takes care of the initial transaction sync for each new
-// connection. When a new peer appears, we relay all currently pending
-// transactions. In order to minimise egress bandwidth usage, we send
-// the transactions in small packs to one peer at a time.
-func (h *handler) txsyncLoop64() {
- defer h.wg.Done()
-
- var (
- pending = make(map[enode.ID]*txsync)
- sending = false // whether a send is active
- pack = new(txsync) // the pack that is being sent
- done = make(chan error, 1) // result of the send
- )
-
- // send starts a sending a pack of transactions from the sync.
- send := func(s *txsync) {
- if s.p.Version() >= eth.ETH65 {
- panic("initial transaction syncer running on eth/65+")
- }
- // Fill pack with transactions up to the target size.
- size := common.StorageSize(0)
- pack.p = s.p
- pack.txs = pack.txs[:0]
- for i := 0; i < len(s.txs) && size < txsyncPackSize; i++ {
- pack.txs = append(pack.txs, s.txs[i])
- size += s.txs[i].Size()
- }
- // Remove the transactions that will be sent.
- s.txs = s.txs[:copy(s.txs, s.txs[len(pack.txs):])]
- if len(s.txs) == 0 {
- delete(pending, s.p.Peer.ID())
- }
- // Send the pack in the background.
- s.p.Log().Trace("Sending batch of transactions", "count", len(pack.txs), "bytes", size)
- sending = true
- go func() { done <- pack.p.SendTransactions(pack.txs) }()
- }
- // pick chooses the next pending sync.
- pick := func() *txsync {
- if len(pending) == 0 {
- return nil
- }
- n := rand.Intn(len(pending)) + 1
- for _, s := range pending {
- if n--; n == 0 {
- return s
- }
- }
- return nil
- }
-
- for {
- select {
- case s := <-h.txsyncCh:
- pending[s.p.Peer.ID()] = s
- if !sending {
- send(s)
- }
- case err := <-done:
- sending = false
- // Stop tracking peers that cause send failures.
- if err != nil {
- pack.p.Log().Debug("Transaction send failed", "err", err)
- delete(pending, pack.p.Peer.ID())
- }
- // Schedule the next send.
- if s := pick(); s != nil {
- send(s)
- }
- case <-h.quitSync:
- return
- }
+ hashes := make([]common.Hash, len(txs))
+ for i, tx := range txs {
+ hashes[i] = tx.Hash()
}
+ p.AsyncSendPooledTransactionHashes(hashes)
}
// chainSyncer coordinates blockchain sync components.
@@ -276,7 +182,7 @@ func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, *big.Int) {
// If we're in fast sync mode, return that directly
if atomic.LoadUint32(&cs.handler.fastSync) == 1 {
block := cs.handler.chain.CurrentFastBlock()
- td := cs.handler.chain.GetTdByHash(block.Hash())
+ td := cs.handler.chain.GetTd(block.Hash(), block.NumberU64())
return downloader.FastSync, td
}
// We are probably in full sync, but we might have rewound to before the
@@ -284,7 +190,7 @@ func (cs *chainSyncer) modeAndLocalHead() (downloader.SyncMode, *big.Int) {
if pivot := rawdb.ReadLastPivotNumber(cs.handler.database); pivot != nil {
if head := cs.handler.chain.CurrentBlock(); head.NumberU64() < *pivot {
block := cs.handler.chain.CurrentFastBlock()
- td := cs.handler.chain.GetTdByHash(block.Hash())
+ td := cs.handler.chain.GetTd(block.Hash(), block.NumberU64())
return downloader.FastSync, td
}
}
diff --git a/eth/sync_test.go b/eth/sync_test.go
index a0c6f86023..e96b9ee81f 100644
--- a/eth/sync_test.go
+++ b/eth/sync_test.go
@@ -28,7 +28,6 @@ import (
)
// Tests that fast sync is disabled after a successful sync cycle.
-func TestFastSyncDisabling65(t *testing.T) { testFastSyncDisabling(t, eth.ETH65) }
func TestFastSyncDisabling66(t *testing.T) { testFastSyncDisabling(t, eth.ETH66) }
// Tests that fast sync gets disabled as soon as a real block is successfully
diff --git a/eth/tracers/api.go b/eth/tracers/api.go
index 6826593285..5019fb6f73 100644
--- a/eth/tracers/api.go
+++ b/eth/tracers/api.go
@@ -290,7 +290,11 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
}()
}
// Start a goroutine to feed all the blocks into the tracers
- begin := time.Now()
+ var (
+ begin = time.Now()
+ derefTodo []common.Hash // list of hashes to dereference from the db
+ derefsMu sync.Mutex // mutex for the derefs
+ )
go func() {
var (
@@ -324,6 +328,14 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
return
default:
}
+ // clean out any derefs
+ derefsMu.Lock()
+ for _, h := range derefTodo {
+ statedb.Database().TrieDB().Dereference(h)
+ }
+ derefTodo = derefTodo[:0]
+ derefsMu.Unlock()
+
// Print progress logs if long enough time elapsed
if time.Since(logged) > 8*time.Second {
logged = time.Now()
@@ -382,12 +394,11 @@ func (api *API) traceChain(ctx context.Context, start, end *types.Block, config
Hash: res.block.Hash(),
Traces: res.results,
}
+ // Schedule any parent tries held in memory by this task for dereferencing
done[uint64(result.Block)] = result
-
- // Dereference any parent tries held in memory by this task
- if res.statedb.Database().TrieDB() != nil {
- res.statedb.Database().TrieDB().Dereference(res.rootref)
- }
+ derefsMu.Lock()
+ derefTodo = append(derefTodo, res.rootref)
+ derefsMu.Unlock()
// Stream completed traces to the user, aborting on the first error
for result, ok := done[next]; ok; result, ok = done[next] {
if len(result.Traces) > 0 || next == end.NumberU64() {
@@ -445,12 +456,11 @@ func (api *API) TraceBlockFromFile(ctx context.Context, file string, config *Tra
// EVM against a block pulled from the pool of bad ones and returns them as a JSON
// object.
func (api *API) TraceBadBlock(ctx context.Context, hash common.Hash, config *TraceConfig) ([]*txTraceResult, error) {
- for _, block := range rawdb.ReadAllBadBlocks(api.backend.ChainDb()) {
- if block.Hash() == hash {
- return api.traceBlock(ctx, block, config)
- }
+ block := rawdb.ReadBadBlock(api.backend.ChainDb(), hash)
+ if block == nil {
+ return nil, fmt.Errorf("bad block %#x not found", hash)
}
- return nil, fmt.Errorf("bad block %#x not found", hash)
+ return api.traceBlock(ctx, block, config)
}
// StandardTraceBlockToFile dumps the structured logs created during the
@@ -464,16 +474,72 @@ func (api *API) StandardTraceBlockToFile(ctx context.Context, hash common.Hash,
return api.standardTraceBlockToFile(ctx, block, config)
}
+// IntermediateRoots executes a block (bad- or canon- or side-), and returns a list
+// of intermediate roots: the stateroot after each transaction.
+func (api *API) IntermediateRoots(ctx context.Context, hash common.Hash, config *TraceConfig) ([]common.Hash, error) {
+ block, _ := api.blockByHash(ctx, hash)
+ if block == nil {
+ // Check in the bad blocks
+ block = rawdb.ReadBadBlock(api.backend.ChainDb(), hash)
+ }
+ if block == nil {
+ return nil, fmt.Errorf("block %#x not found", hash)
+ }
+ if block.NumberU64() == 0 {
+ return nil, errors.New("genesis is not traceable")
+ }
+ parent, err := api.blockByNumberAndHash(ctx, rpc.BlockNumber(block.NumberU64()-1), block.ParentHash())
+ if err != nil {
+ return nil, err
+ }
+ reexec := defaultTraceReexec
+ if config != nil && config.Reexec != nil {
+ reexec = *config.Reexec
+ }
+ statedb, err := api.backend.StateAtBlock(ctx, parent, reexec, nil, true)
+ if err != nil {
+ return nil, err
+ }
+ var (
+ roots []common.Hash
+ signer = types.MakeSigner(api.backend.ChainConfig(), block.Number())
+ chainConfig = api.backend.ChainConfig()
+ vmctx = core.NewEVMBlockContext(block.Header(), api.chainContext(ctx), nil)
+ deleteEmptyObjects = chainConfig.IsEIP158(block.Number())
+ )
+ for i, tx := range block.Transactions() {
+ var (
+ msg, _ = tx.AsMessage(signer, block.BaseFee())
+ txContext = core.NewEVMTxContext(msg)
+ vmenv = vm.NewEVM(vmctx, txContext, statedb, chainConfig, vm.Config{})
+ )
+ statedb.Prepare(tx.Hash(), i)
+ if _, err := core.ApplyMessage(vmenv, msg, new(core.GasPool).AddGas(msg.Gas())); err != nil {
+ log.Warn("Tracing intermediate roots did not complete", "txindex", i, "txhash", tx.Hash(), "err", err)
+ // We intentionally don't return the error here: if we do, then the RPC server will not
+ // return the roots. Most likely, the caller already knows that a certain transaction fails to
+ // be included, but still want the intermediate roots that led to that point.
+ // It may happen the tx_N causes an erroneous state, which in turn causes tx_N+M to not be
+ // executable.
+ // N.B: This should never happen while tracing canon blocks, only when tracing bad blocks.
+ return roots, nil
+ }
+ // calling IntermediateRoot will internally call Finalize on the state
+ // so any modifications are written to the trie
+ roots = append(roots, statedb.IntermediateRoot(deleteEmptyObjects))
+ }
+ return roots, nil
+}
+
// StandardTraceBadBlockToFile dumps the structured logs created during the
// execution of EVM against a block pulled from the pool of bad ones to the
// local file system and returns a list of files to the caller.
func (api *API) StandardTraceBadBlockToFile(ctx context.Context, hash common.Hash, config *StdTraceConfig) ([]string, error) {
- for _, block := range rawdb.ReadAllBadBlocks(api.backend.ChainDb()) {
- if block.Hash() == hash {
- return api.standardTraceBlockToFile(ctx, block, config)
- }
+ block := rawdb.ReadBadBlock(api.backend.ChainDb(), hash)
+ if block == nil {
+ return nil, fmt.Errorf("bad block %#x not found", hash)
}
- return nil, fmt.Errorf("bad block %#x not found", hash)
+ return api.standardTraceBlockToFile(ctx, block, config)
}
// traceBlock configures a new tracer according to the provided configuration, and
diff --git a/eth/tracers/internal/tracers/4byte_tracer.js b/eth/tracers/internal/tracers/4byte_tracer.js
index 462b4ad4cb..9ec3209f8b 100644
--- a/eth/tracers/internal/tracers/4byte_tracer.js
+++ b/eth/tracers/internal/tracers/4byte_tracer.js
@@ -31,48 +31,27 @@
// ids aggregates the 4byte ids found.
ids : {},
- // callType returns 'false' for non-calls, or the peek-index for the first param
- // after 'value', i.e. meminstart.
- callType: function(opstr){
- switch(opstr){
- case "CALL": case "CALLCODE":
- // gas, addr, val, memin, meminsz, memout, memoutsz
- return 3; // stack ptr to memin
-
- case "DELEGATECALL": case "STATICCALL":
- // gas, addr, memin, meminsz, memout, memoutsz
- return 2; // stack ptr to memin
- }
- return false;
- },
-
// store save the given indentifier and datasize.
store: function(id, size){
var key = "" + toHex(id) + "-" + size;
this.ids[key] = this.ids[key] + 1 || 1;
},
- // step is invoked for every opcode that the VM executes.
- step: function(log, db) {
- // Skip any opcodes that are not internal calls
- var ct = this.callType(log.op.toString());
- if (!ct) {
- return;
- }
+ enter: function(frame) {
// Skip any pre-compile invocations, those are just fancy opcodes
- if (isPrecompiled(toAddress(log.stack.peek(1).toString(16)))) {
+ if (isPrecompiled(frame.getTo())) {
return;
}
- // Gather internal call details
- var inSz = log.stack.peek(ct + 1).valueOf();
- if (inSz >= 4) {
- var inOff = log.stack.peek(ct).valueOf();
- this.store(log.memory.slice(inOff, inOff + 4), inSz-4);
+ var input = frame.getInput()
+ if (input.length >= 4) {
+ this.store(slice(input, 0, 4), input.length - 4);
}
},
+ exit: function(frameResult) {},
+
// fault is invoked when the actual execution of an opcode fails.
- fault: function(log, db) { },
+ fault: function(log, db) {},
// result is invoked when all the opcodes have been iterated over and returns
// the final result of the tracing.
diff --git a/eth/tracers/internal/tracers/4byte_tracer_legacy.js b/eth/tracers/internal/tracers/4byte_tracer_legacy.js
new file mode 100644
index 0000000000..462b4ad4cb
--- /dev/null
+++ b/eth/tracers/internal/tracers/4byte_tracer_legacy.js
@@ -0,0 +1,86 @@
+// 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 .
+
+// 4byteTracer searches for 4byte-identifiers, and collects them for post-processing.
+// It collects the methods identifiers along with the size of the supplied data, so
+// a reversed signature can be matched against the size of the data.
+//
+// Example:
+// > debug.traceTransaction( "0x214e597e35da083692f5386141e69f47e973b2c56e7a8073b1ea08fd7571e9de", {tracer: "4byteTracer"})
+// {
+// 0x27dc297e-128: 1,
+// 0x38cc4831-0: 2,
+// 0x524f3889-96: 1,
+// 0xadf59f99-288: 1,
+// 0xc281d19e-0: 1
+// }
+{
+ // ids aggregates the 4byte ids found.
+ ids : {},
+
+ // callType returns 'false' for non-calls, or the peek-index for the first param
+ // after 'value', i.e. meminstart.
+ callType: function(opstr){
+ switch(opstr){
+ case "CALL": case "CALLCODE":
+ // gas, addr, val, memin, meminsz, memout, memoutsz
+ return 3; // stack ptr to memin
+
+ case "DELEGATECALL": case "STATICCALL":
+ // gas, addr, memin, meminsz, memout, memoutsz
+ return 2; // stack ptr to memin
+ }
+ return false;
+ },
+
+ // store save the given indentifier and datasize.
+ store: function(id, size){
+ var key = "" + toHex(id) + "-" + size;
+ this.ids[key] = this.ids[key] + 1 || 1;
+ },
+
+ // step is invoked for every opcode that the VM executes.
+ step: function(log, db) {
+ // Skip any opcodes that are not internal calls
+ var ct = this.callType(log.op.toString());
+ if (!ct) {
+ return;
+ }
+ // Skip any pre-compile invocations, those are just fancy opcodes
+ if (isPrecompiled(toAddress(log.stack.peek(1).toString(16)))) {
+ return;
+ }
+ // Gather internal call details
+ var inSz = log.stack.peek(ct + 1).valueOf();
+ if (inSz >= 4) {
+ var inOff = log.stack.peek(ct).valueOf();
+ this.store(log.memory.slice(inOff, inOff + 4), inSz-4);
+ }
+ },
+
+ // fault is invoked when the actual execution of an opcode fails.
+ fault: function(log, db) { },
+
+ // result is invoked when all the opcodes have been iterated over and returns
+ // the final result of the tracing.
+ result: function(ctx) {
+ // Save the outer calldata also
+ if (ctx.input.length >= 4) {
+ this.store(slice(ctx.input, 0, 4), ctx.input.length-4)
+ }
+ return this.ids;
+ },
+}
diff --git a/eth/tracers/internal/tracers/assets.go b/eth/tracers/internal/tracers/assets.go
index 7f45ab286e..185967bdb6 100644
--- a/eth/tracers/internal/tracers/assets.go
+++ b/eth/tracers/internal/tracers/assets.go
@@ -1,8 +1,10 @@
// Code generated by go-bindata. DO NOT EDIT.
// sources:
-// 4byte_tracer.js (2.933kB)
+// 4byte_tracer.js (2.224kB)
+// 4byte_tracer_legacy.js (2.933kB)
// bigram_tracer.js (1.712kB)
-// call_tracer.js (8.956kB)
+// call_tracer.js (3.497kB)
+// call_tracer_legacy.js (8.956kB)
// evmdis_tracer.js (4.195kB)
// noop_tracer.js (1.271kB)
// opcount_tracer.js (1.372kB)
@@ -77,7 +79,7 @@ func (fi bindataFileInfo) Sys() interface{} {
return nil
}
-var __4byte_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x56\x5b\x6f\xdb\x4a\x0e\x7e\xb6\x7f\x05\xd7\x2f\xb5\x51\x59\x8e\x2f\x89\x2f\xd9\x16\xf0\xe6\xa4\x6d\x80\x9c\x24\x88\xdd\x3d\x28\x16\xfb\x30\x9e\xa1\xac\xd9\xc8\x33\xc2\x0c\xe5\x4b\x73\xf2\xdf\x17\x1c\x49\x89\x93\xd3\x62\xbb\x4f\x96\x47\xc3\x8f\x1f\xc9\x8f\xa4\x7a\x3d\xb8\xb0\xf9\xc1\xe9\x75\x4a\x30\x38\xe9\x8f\x61\x99\x22\xac\x6d\x17\x29\x45\x87\xc5\x06\xe6\x05\xa5\xd6\xf9\x66\xaf\x07\xcb\x54\x7b\x48\x74\x86\xa0\x3d\xe4\xc2\x11\xd8\x04\xe8\xcd\xfd\x4c\xaf\x9c\x70\x87\xb8\xd9\xeb\x95\x36\x3f\x7c\xcd\x08\x89\x43\x04\x6f\x13\xda\x09\x87\x33\x38\xd8\x02\xa4\x30\xe0\x50\x69\x4f\x4e\xaf\x0a\x42\xd0\x04\xc2\xa8\x9e\x75\xb0\xb1\x4a\x27\x07\x86\xd4\x04\x85\x51\xe8\x82\x6b\x42\xb7\xf1\x35\x8f\xcf\x37\x5f\xe1\x1a\xbd\x47\x07\x9f\xd1\xa0\x13\x19\xdc\x15\xab\x4c\x4b\xb8\xd6\x12\x8d\x47\x10\x1e\x72\x3e\xf1\x29\x2a\x58\x05\x38\x36\xfc\xc4\x54\x16\x15\x15\xf8\x64\x0b\xa3\x04\x69\x6b\x22\x40\xcd\xcc\x61\x8b\xce\x6b\x6b\x60\x58\xbb\xaa\x00\x23\xb0\x8e\x41\xda\x82\x38\x00\x07\x36\x67\xbb\x0e\x08\x73\x80\x4c\xd0\x8b\xe9\x2f\x24\xe4\x25\x6e\x05\xda\x04\x37\xa9\xcd\x11\x28\x15\xc4\x51\xef\x74\x96\xc1\x0a\xa1\xf0\x98\x14\x59\xc4\x68\xab\x82\xe0\x8f\xab\xe5\x97\xdb\xaf\x4b\x98\xdf\x7c\x83\x3f\xe6\xf7\xf7\xf3\x9b\xe5\xb7\x73\xd8\x69\x4a\x6d\x41\x80\x5b\x2c\xa1\xf4\x26\xcf\x34\x2a\xd8\x09\xe7\x84\xa1\x03\xd8\x84\x11\x7e\xbf\xbc\xbf\xf8\x32\xbf\x59\xce\xff\x71\x75\x7d\xb5\xfc\x06\xd6\xc1\xa7\xab\xe5\xcd\xe5\x62\x01\x9f\x6e\xef\x61\x0e\x77\xf3\xfb\xe5\xd5\xc5\xd7\xeb\xf9\x3d\xdc\x7d\xbd\xbf\xbb\x5d\x5c\xc6\xb0\x40\x66\x85\x6c\xff\xbf\x73\x9e\x84\xea\x39\x04\x85\x24\x74\xe6\xeb\x4c\x7c\xb3\x05\xf8\xd4\x16\x99\x82\x54\x6c\x11\x1c\x4a\xd4\x5b\x54\x20\x40\xda\xfc\xf0\xcb\x45\x65\x2c\x91\x59\xb3\x0e\x31\xff\x54\x90\x70\x95\x80\xb1\x14\x81\x47\x84\xbf\xa7\x44\xf9\xac\xd7\xdb\xed\x76\xf1\xda\x14\xb1\x75\xeb\x5e\x56\xc2\xf9\xde\xc7\xb8\xc9\x98\xa3\xd5\x81\x70\xe9\x84\x44\x07\x1e\x85\x93\x29\xfa\x10\x4c\x78\xd1\xd5\x0a\x0d\xe9\x44\xa3\xf3\x11\x8b\x14\xa4\xcd\x32\x94\xe4\x99\xc1\x26\x5c\xcc\xad\xa7\x6e\xee\xac\x44\xef\xb5\x59\x73\xe0\x70\x45\xaf\x2e\xc2\x06\x29\xb5\xca\xc3\x11\xdc\xdb\x68\xbc\xfe\x8e\x75\x36\x7c\x91\x97\x65\x54\x82\x44\x04\xde\x86\xe8\xc1\x21\xcb\x0c\x15\x78\xbd\x36\x82\x0a\x87\xa1\x97\x56\x08\x1b\x41\x92\xc5\x2e\xd6\x42\x1b\x4f\x7f\x01\x64\x9c\xba\x22\x97\x7b\xb1\xc9\x33\x9c\xf1\x33\xc0\x47\x50\xb8\x2a\xd6\x31\x71\x0a\x96\x4e\x18\x2f\x24\x8b\xbb\x0d\xad\x93\xfd\xa0\x3f\xc2\xd3\xe9\x18\x87\xa7\x4a\x9c\x4c\x86\x67\xd3\x41\x72\x3a\x9c\x9c\xf5\x47\x7d\x3c\x9b\x26\xa3\x31\x4e\xc7\xc3\xd5\x40\x9e\x9e\xe1\x58\x4c\x4e\xc6\xc3\x55\x1f\xc5\xc9\x24\x51\xe3\xd3\x71\x1f\xa7\x0a\x5b\x11\x3c\x06\x60\x37\x83\xd6\x51\xa6\x5b\x4f\x9d\xd2\xfb\x63\xf9\x03\x70\xb2\x1f\x8c\x95\x1c\x4c\xc7\xd8\xed\x0f\x26\x33\xe8\x47\x2f\x6f\x86\x13\x29\x47\x93\x61\xbf\x7b\x32\x83\xc1\xd1\xf9\xe9\x60\x94\x0c\x27\x93\x69\x77\x7a\xf6\xda\x40\xa8\xe4\x74\x9a\x4c\xa7\xdd\xc1\xe4\x0d\x94\x1c\x4c\xfa\xaa\x3f\x45\x86\xea\x97\xc7\x4f\xcd\xc7\x66\x83\x07\x8e\xf2\x20\xd6\x6b\x87\x6b\x41\x58\x56\x2d\x30\x0e\x2f\x12\x1e\x16\x71\xb3\xc1\xcf\x33\x78\x7c\x8a\x9a\xc1\x46\x8a\x2c\x5b\x1e\x72\x56\x35\x15\xce\x78\x78\x97\x88\xcc\xe3\xbb\xa0\x0b\x63\x4d\x97\x2f\x78\x1e\x1f\x01\x2f\x47\x7c\xe8\x6a\xa3\x70\x1f\x2e\xf0\x51\xa2\x9d\x27\x1e\xb3\x62\x13\x10\x45\xc2\xd3\xe4\xdd\x56\x64\x05\xbe\x8b\x40\xc7\x18\xc3\x06\x37\x5c\x54\xe1\x28\x6e\x36\x6a\x97\x33\x48\x0a\x53\x56\xca\xe6\x9e\x5c\xe7\xb1\xd9\x68\xf8\x9d\x26\x99\x1e\x1d\x48\xe1\x11\x5a\x17\xf3\xeb\xeb\xd6\x0c\x5e\xfe\x5c\xdc\xfe\x76\xd9\x9a\x35\x1b\x0d\x76\xb9\x16\x2c\x6d\xa5\x5c\x04\x5b\x91\x45\xa5\xbb\xea\xc7\x7f\x0f\x0f\xb6\xa0\xfa\xd7\x7f\x67\xb3\x32\x5e\x18\x9e\x43\xaf\x07\x9e\x84\x7c\x80\x9c\x1c\x90\x2d\xcd\x9a\xcf\xae\x7f\xbb\xbc\xbe\xfc\x3c\x5f\x5e\xbe\xa2\xb0\x58\xce\x97\x57\x17\xe5\xd1\x5f\x49\xfc\x1f\xfe\x07\x3f\xf3\xdf\x68\x3c\x35\x9f\x6f\x85\x9a\x9c\x37\x1b\x75\xd5\x3c\xf1\x9c\xf2\x3c\x8d\xc2\x18\xd1\x3c\x3c\xb9\x2c\x55\x6b\x86\x3e\xe7\x8e\xe1\x0e\x8a\x9b\x8d\x70\xff\x28\xdf\x5a\x45\xa1\xb9\x42\x86\xb7\xc2\xc1\x03\x1e\xe0\x03\xb4\x5a\xf0\x1e\xc8\x7e\xc1\x7d\x5b\xab\x0e\xbc\x87\x56\x97\x4f\xf8\xe6\x79\xb3\xd1\xa0\x54\xfb\x58\x2b\xff\xaf\x07\x3c\xfc\x1b\x3e\xc0\xeb\xff\xef\xa1\x0f\x7f\xfe\x09\xfd\x57\x34\x31\xe7\x85\xa1\xcd\xd6\x3e\xa0\x0a\x92\xe1\x01\x70\x00\x9b\x4b\xab\xaa\x8d\xc1\x11\xfc\xf3\x77\xc0\x3d\xca\x82\xd0\x07\xba\x98\x1f\xb1\xcd\xec\x3a\x02\xb5\xea\x00\xb3\xed\xf5\x60\xf1\xa0\xf3\xb0\xb8\x4a\x14\x5f\xc2\xf0\x46\x34\x96\x40\x1b\x42\x67\x44\x16\xa4\xed\xab\xf8\x24\xd5\x7c\x6b\xf5\x31\x6a\x6c\xf3\x98\xec\x82\x9c\x36\xeb\x76\xa7\xc3\x31\xea\x04\xda\x7f\x93\x54\xfa\xaa\xd2\x7f\x5e\x15\xe3\xd8\x75\xee\xb0\x2b\xed\x26\x0f\x5f\x19\x66\x6b\x65\xd8\xc3\x3e\x02\x4a\x2d\xef\x6f\x87\xf0\x9f\xc2\x13\x24\xc2\xc8\x67\xa2\x15\xbe\xf6\x77\x0e\x2b\x63\xd5\x26\x3b\x57\xca\xa1\xf7\x81\x51\x50\x42\xcc\x6d\xd6\xee\x77\x5e\xc8\xf5\xcf\x3a\x9d\xce\xcf\x48\x7d\x16\x61\xf7\xbf\x0a\xbc\x5e\x62\x55\xfc\xda\x2c\xbe\xc3\x07\x78\xe3\x41\x12\x57\xad\x13\x87\x5e\xbd\x4d\xda\xcf\x19\x08\xd7\x3f\x7e\x80\x51\xe5\xb2\x84\xb8\x4d\x92\x1f\x61\xbc\xb1\x2f\x65\x12\x14\x17\x22\x62\xd1\xbb\x43\xec\x79\x6d\xb5\x03\x48\x54\x61\xbd\x87\x51\x27\x0a\xd4\xba\xa3\x4e\x15\x4f\x2d\x9d\x44\x14\x19\x1d\x6b\x67\x97\x56\xdf\x07\x42\x52\x21\xb2\x4a\x2e\xfc\xad\x63\x13\x10\xa6\x56\x54\x52\x6e\xee\x46\xb0\xff\xa1\x86\xa0\x76\xe1\xd0\xff\xc8\x07\x27\x8f\xfd\xd4\xe2\x0a\x3b\x7f\x85\xdc\x60\x84\x4e\xf0\x47\x8f\xdd\x56\x2d\x56\x0d\xcd\x00\x57\xce\x42\xce\x7f\x05\x5c\x2d\x2e\xde\x1e\x61\xa9\x36\xca\xf3\x23\x52\x92\xf6\x2f\xa2\xae\x9b\xd9\x16\x3c\x3f\xb9\x86\xdc\xc0\x20\x32\x6f\xab\xaa\x48\xda\xc7\xda\xe4\x05\xc5\x19\x9a\x35\xa5\xc7\x15\x3a\x4a\x7a\x99\xe9\xe7\xcb\x11\x9c\x44\x21\xd1\x6f\xcd\xbb\xa3\xce\xeb\x29\x53\xf7\x73\xd9\xc1\x4f\xcd\xff\x06\x00\x00\xff\xff\x8e\xc8\x27\x72\x75\x0b\x00\x00")
+var __4byte_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x55\x5b\x6f\x22\x39\x13\x7d\x86\x5f\x71\xc4\x13\x68\x9a\x4b\x73\x09\x97\xf9\x32\x12\xdf\x28\x99\x41\xca\x66\x22\x42\x34\x8a\x56\xfb\x60\xda\xd5\xdd\xde\x18\xbb\x65\xbb\xb9\x6c\x26\xff\x7d\x65\x37\xe4\x36\xbb\xda\x79\x02\xec\xaa\x73\xaa\x4e\x1d\x17\xdd\x2e\x3e\xeb\xe2\x60\x44\x96\x3b\xf4\x7b\xf1\x18\xab\x9c\x90\xe9\x36\xb9\x9c\x0c\x95\x1b\xcc\x4b\x97\x6b\x63\xeb\xdd\x2e\x56\xb9\xb0\x48\x85\x24\x08\x8b\x82\x19\x07\x9d\xc2\xbd\x8b\x97\x62\x6d\x98\x39\x74\xea\xdd\x6e\x95\xf3\x8f\xd7\x1e\x21\x35\x44\xb0\x3a\x75\x3b\x66\x68\x86\x83\x2e\x91\x30\x05\x43\x5c\x58\x67\xc4\xba\x74\x04\xe1\xc0\x14\xef\x6a\x83\x8d\xe6\x22\x3d\x78\x48\xe1\x50\x2a\x4e\x26\x50\x3b\x32\x1b\x7b\xaa\xe3\xcb\xf5\x1d\xae\xc8\x5a\x32\xf8\x42\x8a\x0c\x93\xb8\x29\xd7\x52\x24\xb8\x12\x09\x29\x4b\x60\x16\x85\x3f\xb1\x39\x71\xac\x03\x9c\x4f\xbc\xf4\xa5\xdc\x1e\x4b\xc1\xa5\x2e\x15\x67\x4e\x68\x15\x81\x84\xaf\x1c\x5b\x32\x56\x68\x85\xc1\x89\xea\x08\x18\x41\x1b\x0f\xd2\x64\xce\x37\x60\xa0\x0b\x9f\xd7\x02\x53\x07\x48\xe6\x5e\x52\x7f\x41\x90\x97\xbe\x39\x84\x0a\x34\xb9\x2e\x08\x2e\x67\xce\x77\xbd\x13\x52\x62\x4d\x28\x2d\xa5\xa5\x8c\x3c\xda\xba\x74\xf8\xbe\x58\x7d\xfd\x76\xb7\xc2\xfc\xfa\x1e\xdf\xe7\xcb\xe5\xfc\x7a\x75\xff\x11\x3b\xe1\x72\x5d\x3a\xd0\x96\x2a\x28\xb1\x29\xa4\x20\x8e\x1d\x33\x86\x29\x77\x80\x4e\x3d\xc2\x6f\x17\xcb\xcf\x5f\xe7\xd7\xab\xf9\xff\x17\x57\x8b\xd5\x3d\xb4\xc1\xe5\x62\x75\x7d\x71\x7b\x8b\xcb\x6f\x4b\xcc\x71\x33\x5f\xae\x16\x9f\xef\xae\xe6\x4b\xdc\xdc\x2d\x6f\xbe\xdd\x5e\x74\x70\x4b\xbe\x2a\xf2\xf9\xff\xad\x79\x1a\xa6\x67\x08\x9c\x1c\x13\xd2\x9e\x94\xb8\xd7\x25\x6c\xae\x4b\xc9\x91\xb3\x2d\xc1\x50\x42\x62\x4b\x1c\x0c\x89\x2e\x0e\xbf\x3c\x54\x8f\xc5\xa4\x56\x59\xe8\xf9\x5f\x0d\x89\x45\x0a\xa5\x5d\x04\x4b\x84\xff\xe5\xce\x15\xb3\x6e\x77\xb7\xdb\x75\x32\x55\x76\xb4\xc9\xba\xb2\x82\xb3\xdd\x4f\x9d\xba\xc7\x1c\xae\x0f\x8e\x56\x86\x25\x64\x60\x89\x99\x24\x27\x1b\x9a\x09\x17\x6d\xc1\x49\x39\x91\x0a\x32\x36\xf2\x26\x45\xa2\xa5\xa4\xc4\x59\x5f\xc1\x26\x04\x16\xda\xba\x76\x61\x74\x42\xd6\x0a\x95\xf9\xc6\xb1\x70\x6f\x02\xb1\x21\x97\x6b\x6e\xf1\x0a\xee\x7d\x37\x56\xfc\x45\x27\x35\x6c\x59\x54\x63\xe4\xcc\xb1\x08\x56\x87\xee\x61\xc8\xdb\x8c\x38\xac\xc8\x14\x73\xa5\xa1\xf0\x96\xd6\x84\x0d\x73\x89\x37\x3b\xcb\x98\x50\xd6\xfd\x04\xe8\x71\x4e\x13\xb9\xd8\xb3\x4d\x21\x69\xe6\xbf\x03\x9f\xc0\x69\x5d\x66\x1d\xe7\x25\x58\x19\xa6\x2c\x4b\xbc\xb9\x9b\x68\xf4\xf6\xfd\x78\x48\xa3\xe9\x98\x06\x23\xce\x7a\x93\xc1\xd9\xb4\x9f\x8e\x06\x93\xb3\x78\x18\xd3\xd9\x34\x1d\x8e\x69\x3a\x1e\xac\xfb\xc9\xe8\x8c\xc6\x6c\xd2\x1b\x0f\xd6\x31\xb1\xde\x24\xe5\xe3\xd1\x38\xa6\x29\xa7\x46\x84\xc7\x00\x6c\x66\x68\xbc\x52\xba\xf1\xd4\xaa\xd8\x1f\xab\x0f\xa0\xb7\xef\x8f\x79\xd2\x9f\x8e\xa9\x1d\xf7\x27\x33\xc4\xd1\xcb\xcd\x60\x92\x24\xc3\xc9\x20\x6e\xf7\x66\xe8\xbf\x3a\x1f\xf5\x87\xe9\x60\x32\x99\xb6\xa7\x67\x6f\x13\x18\x4f\x47\xd3\x74\x3a\x6d\xf7\x27\xef\xa0\x92\xfe\x24\xe6\xf1\x94\x3c\x54\x5c\x1d\x3f\xd5\x1f\xeb\x35\xbf\x70\xb8\x05\xcb\x32\x43\x19\x73\x54\x4d\x2d\x54\x1c\x2e\x52\xbf\x2c\x3a\xf5\x9a\xff\x3e\xc3\xe3\x53\x54\x0f\x39\xd6\x79\xc7\x5b\xef\xeb\x60\x48\xe1\x9f\xa1\x50\xcf\x43\x0e\x8e\xf1\xda\xfb\x59\x74\xea\xb5\x10\x3f\x43\x5a\xaa\x4a\x63\xc1\xa3\x30\xa6\xd6\x63\xbd\x56\xdb\x32\x83\x07\x3a\xe0\x1c\x8d\x06\x3e\xc0\xe9\xaf\xb4\x6f\x0a\xde\xc2\x07\x34\xda\xfe\xc4\x47\x7e\xac\xd7\x6a\x2e\x17\xb6\x23\xb8\xfd\xfd\x81\x0e\x7f\xe0\x1c\x6f\x7f\x7f\x40\x8c\x1f\x3f\x10\x7f\xac\xd7\x42\x99\xa4\x9c\x97\xff\x99\x33\x35\x6c\x43\x2d\x78\xc6\x6e\x17\xb7\x0f\xa2\x08\x6b\xac\x30\xd4\x4e\xf4\xa6\x08\x8b\x5f\x6d\x75\x12\x56\xa3\x8d\xe0\x72\xed\x57\xaa\x21\xfc\x59\x5a\x87\x94\xa9\xe4\x00\x5d\x24\x9a\x93\xad\xd7\x6a\x22\x45\x53\xd8\x1b\x43\xc7\x64\x5e\x11\x74\x32\x72\x2b\xdd\x6c\xb5\x2a\xa6\x9a\x21\x57\x1a\xe5\xab\x7f\x3a\xb6\x2a\x54\x51\x3a\x9c\xe3\x39\x7c\xe1\x0f\x9a\xad\x13\xa6\xff\xd5\x91\xa4\x32\x97\xe3\xd3\x39\x86\x47\xa0\xd0\x6c\xd0\xb1\x69\xfd\x5b\xae\x02\x23\xf4\x22\x0c\x5b\x11\xde\xa4\xb5\x31\x6c\x1d\x29\x2b\x29\xf6\xc2\xbd\x57\x62\x49\xb6\x94\xae\xf5\x32\xd3\x94\x95\xd2\xf9\x45\xed\x55\x78\xf0\xab\x34\x3f\xee\x56\x96\xb8\x92\x49\xd0\x9e\x92\xd2\x03\xf8\xc7\xc5\xd4\x51\x0b\xa4\xd5\xd6\xab\x85\xfc\x57\x2c\x52\x67\x11\xf8\xfa\x15\x83\x09\x94\x3f\x51\x30\x29\x03\xcd\x51\xdb\x6a\x5d\xae\xc9\x3b\xca\x91\x61\xfe\xff\x42\x6f\x8f\x9e\xaa\xe4\xb4\x01\xce\xe7\xa4\x42\x31\x79\x02\x3e\xbe\x79\xff\xf0\xc2\x3e\xaa\x55\xe7\xaf\x6a\x4a\xdc\xfe\xc5\x01\x27\xf7\xea\xd2\xff\x91\x25\x4c\x4a\xef\x58\x30\x69\xf5\x71\x16\x89\xdb\x77\x7e\x79\x1e\xcf\xc1\xcf\x33\x79\x9f\xde\x1e\xb6\x8e\x3e\xa8\xda\x78\x36\x70\x65\xd9\xa7\xfa\xdf\x01\x00\x00\xff\xff\xf6\xa8\xa1\xb9\xb0\x08\x00\x00")
func _4byte_tracerJsBytes() ([]byte, error) {
return bindataRead(
@@ -93,6 +95,26 @@ func _4byte_tracerJs() (*asset, error) {
}
info := bindataFileInfo{name: "4byte_tracer.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
+ a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x6b, 0xa8, 0x46, 0xa2, 0x3a, 0x2b, 0xaa, 0xb9, 0xb9, 0xba, 0xe2, 0x22, 0x10, 0xe, 0xe7, 0x4c, 0x24, 0xfc, 0x4c, 0x85, 0xeb, 0x96, 0x48, 0xe8, 0x7f, 0xc8, 0xe0, 0xd0, 0xd, 0x26, 0xa1, 0xb2}}
+ return a, nil
+}
+
+var __4byte_tracer_legacyJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x56\x5b\x6f\xdb\x4a\x0e\x7e\xb6\x7f\x05\xd7\x2f\xb5\x51\x59\x8e\x2f\x89\x2f\xd9\x16\xf0\xe6\xa4\x6d\x80\x9c\x24\x88\xdd\x3d\x28\x16\xfb\x30\x9e\xa1\xac\xd9\xc8\x33\xc2\x0c\xe5\x4b\x73\xf2\xdf\x17\x1c\x49\x89\x93\xd3\x62\xbb\x4f\x96\x47\xc3\x8f\x1f\xc9\x8f\xa4\x7a\x3d\xb8\xb0\xf9\xc1\xe9\x75\x4a\x30\x38\xe9\x8f\x61\x99\x22\xac\x6d\x17\x29\x45\x87\xc5\x06\xe6\x05\xa5\xd6\xf9\x66\xaf\x07\xcb\x54\x7b\x48\x74\x86\xa0\x3d\xe4\xc2\x11\xd8\x04\xe8\xcd\xfd\x4c\xaf\x9c\x70\x87\xb8\xd9\xeb\x95\x36\x3f\x7c\xcd\x08\x89\x43\x04\x6f\x13\xda\x09\x87\x33\x38\xd8\x02\xa4\x30\xe0\x50\x69\x4f\x4e\xaf\x0a\x42\xd0\x04\xc2\xa8\x9e\x75\xb0\xb1\x4a\x27\x07\x86\xd4\x04\x85\x51\xe8\x82\x6b\x42\xb7\xf1\x35\x8f\xcf\x37\x5f\xe1\x1a\xbd\x47\x07\x9f\xd1\xa0\x13\x19\xdc\x15\xab\x4c\x4b\xb8\xd6\x12\x8d\x47\x10\x1e\x72\x3e\xf1\x29\x2a\x58\x05\x38\x36\xfc\xc4\x54\x16\x15\x15\xf8\x64\x0b\xa3\x04\x69\x6b\x22\x40\xcd\xcc\x61\x8b\xce\x6b\x6b\x60\x58\xbb\xaa\x00\x23\xb0\x8e\x41\xda\x82\x38\x00\x07\x36\x67\xbb\x0e\x08\x73\x80\x4c\xd0\x8b\xe9\x2f\x24\xe4\x25\x6e\x05\xda\x04\x37\xa9\xcd\x11\x28\x15\xc4\x51\xef\x74\x96\xc1\x0a\xa1\xf0\x98\x14\x59\xc4\x68\xab\x82\xe0\x8f\xab\xe5\x97\xdb\xaf\x4b\x98\xdf\x7c\x83\x3f\xe6\xf7\xf7\xf3\x9b\xe5\xb7\x73\xd8\x69\x4a\x6d\x41\x80\x5b\x2c\xa1\xf4\x26\xcf\x34\x2a\xd8\x09\xe7\x84\xa1\x03\xd8\x84\x11\x7e\xbf\xbc\xbf\xf8\x32\xbf\x59\xce\xff\x71\x75\x7d\xb5\xfc\x06\xd6\xc1\xa7\xab\xe5\xcd\xe5\x62\x01\x9f\x6e\xef\x61\x0e\x77\xf3\xfb\xe5\xd5\xc5\xd7\xeb\xf9\x3d\xdc\x7d\xbd\xbf\xbb\x5d\x5c\xc6\xb0\x40\x66\x85\x6c\xff\xbf\x73\x9e\x84\xea\x39\x04\x85\x24\x74\xe6\xeb\x4c\x7c\xb3\x05\xf8\xd4\x16\x99\x82\x54\x6c\x11\x1c\x4a\xd4\x5b\x54\x20\x40\xda\xfc\xf0\xcb\x45\x65\x2c\x91\x59\xb3\x0e\x31\xff\x54\x90\x70\x95\x80\xb1\x14\x81\x47\x84\xbf\xa7\x44\xf9\xac\xd7\xdb\xed\x76\xf1\xda\x14\xb1\x75\xeb\x5e\x56\xc2\xf9\xde\xc7\xb8\xc9\x98\xa3\xd5\x81\x70\xe9\x84\x44\x07\x1e\x85\x93\x29\xfa\x10\x4c\x78\xd1\xd5\x0a\x0d\xe9\x44\xa3\xf3\x11\x8b\x14\xa4\xcd\x32\x94\xe4\x99\xc1\x26\x5c\xcc\xad\xa7\x6e\xee\xac\x44\xef\xb5\x59\x73\xe0\x70\x45\xaf\x2e\xc2\x06\x29\xb5\xca\xc3\x11\xdc\xdb\x68\xbc\xfe\x8e\x75\x36\x7c\x91\x97\x65\x54\x82\x44\x04\xde\x86\xe8\xc1\x21\xcb\x0c\x15\x78\xbd\x36\x82\x0a\x87\xa1\x97\x56\x08\x1b\x41\x92\xc5\x2e\xd6\x42\x1b\x4f\x7f\x01\x64\x9c\xba\x22\x97\x7b\xb1\xc9\x33\x9c\xf1\x33\xc0\x47\x50\xb8\x2a\xd6\x31\x71\x0a\x96\x4e\x18\x2f\x24\x8b\xbb\x0d\xad\x93\xfd\xa0\x3f\xc2\xd3\xe9\x18\x87\xa7\x4a\x9c\x4c\x86\x67\xd3\x41\x72\x3a\x9c\x9c\xf5\x47\x7d\x3c\x9b\x26\xa3\x31\x4e\xc7\xc3\xd5\x40\x9e\x9e\xe1\x58\x4c\x4e\xc6\xc3\x55\x1f\xc5\xc9\x24\x51\xe3\xd3\x71\x1f\xa7\x0a\x5b\x11\x3c\x06\x60\x37\x83\xd6\x51\xa6\x5b\x4f\x9d\xd2\xfb\x63\xf9\x03\x70\xb2\x1f\x8c\x95\x1c\x4c\xc7\xd8\xed\x0f\x26\x33\xe8\x47\x2f\x6f\x86\x13\x29\x47\x93\x61\xbf\x7b\x32\x83\xc1\xd1\xf9\xe9\x60\x94\x0c\x27\x93\x69\x77\x7a\xf6\xda\x40\xa8\xe4\x74\x9a\x4c\xa7\xdd\xc1\xe4\x0d\x94\x1c\x4c\xfa\xaa\x3f\x45\x86\xea\x97\xc7\x4f\xcd\xc7\x66\x83\x07\x8e\xf2\x20\xd6\x6b\x87\x6b\x41\x58\x56\x2d\x30\x0e\x2f\x12\x1e\x16\x71\xb3\xc1\xcf\x33\x78\x7c\x8a\x9a\xc1\x46\x8a\x2c\x5b\x1e\x72\x56\x35\x15\xce\x78\x78\x97\x88\xcc\xe3\xbb\xa0\x0b\x63\x4d\x97\x2f\x78\x1e\x1f\x01\x2f\x47\x7c\xe8\x6a\xa3\x70\x1f\x2e\xf0\x51\xa2\x9d\x27\x1e\xb3\x62\x13\x10\x45\xc2\xd3\xe4\xdd\x56\x64\x05\xbe\x8b\x40\xc7\x18\xc3\x06\x37\x5c\x54\xe1\x28\x6e\x36\x6a\x97\x33\x48\x0a\x53\x56\xca\xe6\x9e\x5c\xe7\xb1\xd9\x68\xf8\x9d\x26\x99\x1e\x1d\x48\xe1\x11\x5a\x17\xf3\xeb\xeb\xd6\x0c\x5e\xfe\x5c\xdc\xfe\x76\xd9\x9a\x35\x1b\x0d\x76\xb9\x16\x2c\x6d\xa5\x5c\x04\x5b\x91\x45\xa5\xbb\xea\xc7\x7f\x0f\x0f\xb6\xa0\xfa\xd7\x7f\x67\xb3\x32\x5e\x18\x9e\x43\xaf\x07\x9e\x84\x7c\x80\x9c\x1c\x90\x2d\xcd\x9a\xcf\xae\x7f\xbb\xbc\xbe\xfc\x3c\x5f\x5e\xbe\xa2\xb0\x58\xce\x97\x57\x17\xe5\xd1\x5f\x49\xfc\x1f\xfe\x07\x3f\xf3\xdf\x68\x3c\x35\x9f\x6f\x85\x9a\x9c\x37\x1b\x75\xd5\x3c\xf1\x9c\xf2\x3c\x8d\xc2\x18\xd1\x3c\x3c\xb9\x2c\x55\x6b\x86\x3e\xe7\x8e\xe1\x0e\x8a\x9b\x8d\x70\xff\x28\xdf\x5a\x45\xa1\xb9\x42\x86\xb7\xc2\xc1\x03\x1e\xe0\x03\xb4\x5a\xf0\x1e\xc8\x7e\xc1\x7d\x5b\xab\x0e\xbc\x87\x56\x97\x4f\xf8\xe6\x79\xb3\xd1\xa0\x54\xfb\x58\x2b\xff\xaf\x07\x3c\xfc\x1b\x3e\xc0\xeb\xff\xef\xa1\x0f\x7f\xfe\x09\xfd\x57\x34\x31\xe7\x85\xa1\xcd\xd6\x3e\xa0\x0a\x92\xe1\x01\x70\x00\x9b\x4b\xab\xaa\x8d\xc1\x11\xfc\xf3\x77\xc0\x3d\xca\x82\xd0\x07\xba\x98\x1f\xb1\xcd\xec\x3a\x02\xb5\xea\x00\xb3\xed\xf5\x60\xf1\xa0\xf3\xb0\xb8\x4a\x14\x5f\xc2\xf0\x46\x34\x96\x40\x1b\x42\x67\x44\x16\xa4\xed\xab\xf8\x24\xd5\x7c\x6b\xf5\x31\x6a\x6c\xf3\x98\xec\x82\x9c\x36\xeb\x76\xa7\xc3\x31\xea\x04\xda\x7f\x93\x54\xfa\xaa\xd2\x7f\x5e\x15\xe3\xd8\x75\xee\xb0\x2b\xed\x26\x0f\x5f\x19\x66\x6b\x65\xd8\xc3\x3e\x02\x4a\x2d\xef\x6f\x87\xf0\x9f\xc2\x13\x24\xc2\xc8\x67\xa2\x15\xbe\xf6\x77\x0e\x2b\x63\xd5\x26\x3b\x57\xca\xa1\xf7\x81\x51\x50\x42\xcc\x6d\xd6\xee\x77\x5e\xc8\xf5\xcf\x3a\x9d\xce\xcf\x48\x7d\x16\x61\xf7\xbf\x0a\xbc\x5e\x62\x55\xfc\xda\x2c\xbe\xc3\x07\x78\xe3\x41\x12\x57\xad\x13\x87\x5e\xbd\x4d\xda\xcf\x19\x08\xd7\x3f\x7e\x80\x51\xe5\xb2\x84\xb8\x4d\x92\x1f\x61\xbc\xb1\x2f\x65\x12\x14\x17\x22\x62\xd1\xbb\x43\xec\x79\x6d\xb5\x03\x48\x54\x61\xbd\x87\x51\x27\x0a\xd4\xba\xa3\x4e\x15\x4f\x2d\x9d\x44\x14\x19\x1d\x6b\x67\x97\x56\xdf\x07\x42\x52\x21\xb2\x4a\x2e\xfc\xad\x63\x13\x10\xa6\x56\x54\x52\x6e\xee\x46\xb0\xff\xa1\x86\xa0\x76\xe1\xd0\xff\xc8\x07\x27\x8f\xfd\xd4\xe2\x0a\x3b\x7f\x85\xdc\x60\x84\x4e\xf0\x47\x8f\xdd\x56\x2d\x56\x0d\xcd\x00\x57\xce\x42\xce\x7f\x05\x5c\x2d\x2e\xde\x1e\x61\xa9\x36\xca\xf3\x23\x52\x92\xf6\x2f\xa2\xae\x9b\xd9\x16\x3c\x3f\xb9\x86\xdc\xc0\x20\x32\x6f\xab\xaa\x48\xda\xc7\xda\xe4\x05\xc5\x19\x9a\x35\xa5\xc7\x15\x3a\x4a\x7a\x99\xe9\xe7\xcb\x11\x9c\x44\x21\xd1\x6f\xcd\xbb\xa3\xce\xeb\x29\x53\xf7\x73\xd9\xc1\x4f\xcd\xff\x06\x00\x00\xff\xff\x8e\xc8\x27\x72\x75\x0b\x00\x00")
+
+func _4byte_tracer_legacyJsBytes() ([]byte, error) {
+ return bindataRead(
+ __4byte_tracer_legacyJs,
+ "4byte_tracer_legacy.js",
+ )
+}
+
+func _4byte_tracer_legacyJs() (*asset, error) {
+ bytes, err := _4byte_tracer_legacyJsBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ info := bindataFileInfo{name: "4byte_tracer_legacy.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0xb4, 0xc5, 0x48, 0x2d, 0xd9, 0x43, 0x95, 0x93, 0x3b, 0x93, 0x2c, 0x47, 0x8c, 0x84, 0x32, 0x3c, 0x8b, 0x2e, 0xf3, 0x72, 0xc4, 0x57, 0xe6, 0x3a, 0xb3, 0xdf, 0x1d, 0xbf, 0x45, 0x3, 0xfc, 0xa}}
return a, nil
}
@@ -117,7 +139,7 @@ func bigram_tracerJs() (*asset, error) {
return a, nil
}
-var _call_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x5a\xdf\x6f\x1b\x37\xf2\x7f\x96\xfe\x8a\x89\x1f\x6a\x09\x51\x24\x39\xe9\xb7\x5f\xc0\xae\x7a\x50\x1d\x25\x35\xe0\xc6\x81\xad\x34\x08\x82\x3c\x50\xbb\xb3\x12\x6b\x8a\xdc\x92\x5c\xc9\xba\xd6\xff\xfb\x61\x86\xdc\xd5\xae\x24\x3b\xbe\x5e\x71\xe8\xbd\x69\x97\x33\xc3\xe1\xcc\x67\x7e\x71\x35\x18\xc0\xb9\xc9\x37\x56\xce\x17\x1e\x5e\x0e\x4f\xfe\x1f\xa6\x0b\x84\xb9\x79\x81\x7e\x81\x16\x8b\x25\x8c\x0b\xbf\x30\xd6\xb5\x07\x03\x98\x2e\xa4\x83\x4c\x2a\x04\xe9\x20\x17\xd6\x83\xc9\xc0\xef\xd0\x2b\x39\xb3\xc2\x6e\xfa\xed\xc1\x20\xf0\x1c\x5c\x26\x09\x99\x45\x04\x67\x32\xbf\x16\x16\x4f\x61\x63\x0a\x48\x84\x06\x8b\xa9\x74\xde\xca\x59\xe1\x11\xa4\x07\xa1\xd3\x81\xb1\xb0\x34\xa9\xcc\x36\x24\x52\x7a\x28\x74\x8a\x96\xb7\xf6\x68\x97\xae\xd4\xe3\xed\xbb\x0f\x70\x89\xce\xa1\x85\xb7\xa8\xd1\x0a\x05\xef\x8b\x99\x92\x09\x5c\xca\x04\xb5\x43\x10\x0e\x72\x7a\xe3\x16\x98\xc2\x8c\xc5\x11\xe3\x1b\x52\xe5\x26\xaa\x02\x6f\x4c\xa1\x53\xe1\xa5\xd1\x3d\x40\x49\x9a\xc3\x0a\xad\x93\x46\xc3\xab\x72\xab\x28\xb0\x07\xc6\x92\x90\x8e\xf0\x74\x00\x0b\x26\x27\xbe\x2e\x08\xbd\x01\x25\xfc\x96\xf5\x09\x06\xd9\x9e\x3b\x05\xa9\x79\x9b\x85\xc9\x11\xfc\x42\x78\x3a\xf5\x5a\x2a\x05\x33\x84\xc2\x61\x56\xa8\x1e\x49\x9b\x15\x1e\x3e\x5e\x4c\x7f\xba\xfa\x30\x85\xf1\xbb\x4f\xf0\x71\x7c\x7d\x3d\x7e\x37\xfd\x74\x06\x6b\xe9\x17\xa6\xf0\x80\x2b\x0c\xa2\xe4\x32\x57\x12\x53\x58\x0b\x6b\x85\xf6\x1b\x30\x19\x49\xf8\x79\x72\x7d\xfe\xd3\xf8\xdd\x74\xfc\xe3\xc5\xe5\xc5\xf4\x13\x18\x0b\x6f\x2e\xa6\xef\x26\x37\x37\xf0\xe6\xea\x1a\xc6\xf0\x7e\x7c\x3d\xbd\x38\xff\x70\x39\xbe\x86\xf7\x1f\xae\xdf\x5f\xdd\x4c\xfa\x70\x83\xa4\x15\x12\xff\xd7\x6d\x9e\xb1\xf7\x2c\x42\x8a\x5e\x48\xe5\x4a\x4b\x7c\x32\x05\xb8\x85\x29\x54\x0a\x0b\xb1\x42\xb0\x98\xa0\x5c\x61\x0a\x02\x12\x93\x6f\x9e\xec\x54\x92\x25\x94\xd1\x73\x3e\xf3\x83\x80\x84\x8b\x0c\xb4\xf1\x3d\x70\x88\xf0\xfd\xc2\xfb\xfc\x74\x30\x58\xaf\xd7\xfd\xb9\x2e\xfa\xc6\xce\x07\x2a\x88\x73\x83\x1f\xfa\x6d\x92\x99\x08\xa5\xa6\x56\x24\x68\xc9\x39\x02\xb2\x82\xcc\xaf\xcc\x5a\x83\xb7\x42\x3b\x91\x90\xab\xe9\x77\xc2\x60\x14\x1e\xf0\x8e\x9e\xbc\x23\xd0\x82\xc5\xdc\x58\xfa\xad\x54\x89\x33\xa9\x3d\x5a\x2d\x14\xcb\x76\xb0\x14\x29\xc2\x6c\x03\xa2\x2e\xb0\x57\x3f\x0c\xc1\x28\xb8\x1b\xa4\xce\x8c\x5d\x32\x2c\xfb\xed\xdf\xdb\xad\xa8\xa1\xf3\x22\xb9\x25\x05\x49\x7e\x52\x58\x8b\xda\x93\x29\x0b\xeb\xe4\x0a\x99\x04\x02\x4d\xb4\xe7\xe4\x97\x9f\x01\xef\x30\x29\x82\xa4\x56\x25\xe4\x14\x3e\xff\x7e\xff\xa5\xd7\x66\xd1\x29\xba\x04\x75\x8a\x29\x9f\xef\xd6\xc1\x7a\xc1\x16\x85\x35\x1e\xaf\x10\x7e\x2d\x9c\xaf\xd1\x64\xd6\x2c\x41\x68\x30\x05\x21\xbe\x6e\x1d\xa9\xbd\x61\x81\x82\x7e\x6b\xb4\xac\x51\xbf\xdd\xaa\x98\x4f\x21\x13\xca\x61\xdc\xd7\x79\xcc\xe9\x34\x52\xaf\xcc\x2d\x49\x36\x96\x20\x6c\x37\x60\xf2\xc4\xa4\x31\x18\xe8\x1c\xd5\x31\xd0\xf5\xdb\x2d\xe2\x3b\x85\xac\xd0\xbc\x6d\x47\x99\x79\x0f\xd2\x59\x17\x7e\x6f\xb7\x48\xec\xb9\xc8\x7d\x61\x91\xed\x89\xd6\x1a\xeb\x40\x2e\x97\x98\x4a\xe1\x51\x6d\xda\xad\xd6\x4a\xd8\xb0\x00\x23\x50\x66\xde\x9f\xa3\x9f\xd0\x63\xa7\x7b\xd6\x6e\xb5\x64\x06\x9d\xb0\xfa\x6c\x34\xe2\xec\x93\x49\x8d\x69\x10\xdf\xf2\x0b\xe9\xfa\x99\x28\x94\xaf\xf6\x25\xa6\x96\x45\x5f\x58\x4d\x3f\xef\x83\x16\x1f\x11\x8c\x56\x1b\x48\x28\xcb\x88\x19\x85\xa7\xdb\x38\x8f\xcb\x78\x38\xd7\x83\x4c\x38\x32\xa1\xcc\x60\x8d\x90\x5b\x7c\x91\x2c\x90\x7c\xa7\x13\x8c\x5a\xba\x8d\x63\xa7\x8e\x80\x76\xeb\x9b\xbc\xef\xcd\xbb\x62\x39\x43\xdb\xe9\xc2\x37\x30\xbc\xcb\x86\x5d\x18\x8d\xf8\x47\xa9\x7b\xe4\x89\xfa\x92\x14\x93\xc7\x83\x32\xff\x8d\xb7\x52\xcf\xc3\x59\xa3\xae\x17\x19\x08\xd0\xb8\x86\xc4\x68\x06\x35\x79\x65\x86\x52\xcf\x21\xb1\x28\x3c\xa6\x3d\x10\x69\x0a\xde\x04\xe4\x55\x38\x6b\x6e\x09\xdf\x7c\x03\x1d\xda\x6c\x04\xc7\xe7\xd7\x93\xf1\x74\x72\x0c\x7f\xfc\x01\xe1\xcd\x51\x78\xf3\xf2\xa8\x5b\xd3\x4c\xea\xab\x2c\x8b\xca\xb1\xc0\x7e\x8e\x78\xdb\x39\xe9\xf6\x57\x42\x15\x78\x95\x05\x35\x23\xed\x44\xa7\x30\x8a\x3c\xcf\x77\x79\x5e\x36\x78\x88\x69\x30\x80\xb1\x73\xb8\x9c\x29\xdc\x0f\xc8\x18\xb1\x1c\xbc\xce\x53\xc6\x22\xf4\x25\x66\x99\x2b\x24\x54\x95\xbb\x46\xf3\xb3\xc6\x2d\xbf\xc9\xf1\x14\x00\xc0\xe4\x3d\x7e\x41\xb1\xc0\x2f\xbc\xf9\x09\xef\xd8\x47\xa5\x09\x09\x55\xe3\x34\xb5\xe8\x5c\xa7\xdb\x0d\xe4\x52\xe7\x85\x3f\x6d\x90\x2f\x71\x69\xec\xa6\xef\x28\x21\x75\xf8\x68\xbd\x70\xd2\x92\x67\x2e\xdc\x85\x26\x9e\x88\xd4\xb7\xc2\x75\xb6\x4b\xe7\xc6\xf9\xd3\x72\x89\x1e\xca\x35\xb6\x05\xb1\x1d\x0f\xef\x8e\xf7\xad\x35\xec\x6e\x91\x70\xf2\x5d\x97\x58\xee\xcf\x2a\x7c\x57\x69\xa2\x9f\x17\x6e\xd1\x61\x38\x6d\x57\xb7\xa9\x60\x04\xde\x16\x78\x10\xfe\x0c\xa9\x7d\x38\x39\x54\x19\xe5\x12\x6f\x8b\x84\x61\x35\x17\x9c\x69\x38\xd2\x05\x65\x5e\x57\xcc\xd8\xe6\xde\x98\x7d\x74\x45\x70\xdd\x4c\x2e\xdf\xbc\x9e\xdc\x4c\xaf\x3f\x9c\x4f\x8f\x6b\x70\x52\x98\x79\x52\xaa\x79\x06\x85\x7a\xee\x17\xac\x3f\x89\x6b\xae\x7e\x26\x9e\x17\x27\x5f\xc2\x1b\x18\x1d\x08\xf9\xd6\xe3\x1c\xf0\xf9\x0b\xcb\xbe\xdf\x37\x5f\x93\x34\x18\xf3\xaf\x41\x92\x37\x4c\x5c\x92\x7b\x53\x12\x3c\xee\xe7\xbf\x18\x54\xe9\x8c\x28\x7e\x14\x4a\xe8\x04\x1f\xd1\x79\x1f\x6b\xf5\xa4\x79\x20\x0f\x2d\xd1\x2f\x4c\xca\x85\x21\x11\xa1\xb6\x94\x08\x4a\x8d\xc6\x7f\x3f\x1b\x8d\x2f\x2f\x6b\xb9\x88\x9f\xcf\xaf\x5e\xd7\xf3\xd3\xf1\xeb\xc9\xe5\xe4\xed\x78\x3a\xd9\xa5\xbd\x99\x8e\xa7\x17\xe7\xfc\xb6\x4c\x5d\x83\x01\xdc\xdc\xca\x9c\x2b\x0c\xe7\x6d\xb3\xcc\xb9\x55\xae\xf4\x75\x3d\xf0\x0b\x43\x4d\xa8\x8d\x05\x34\x13\x3a\x29\x0b\x9b\x2b\x01\xeb\x0d\xc1\xf5\x21\xe7\x9d\xec\x38\xaf\x82\xb0\x74\xef\x2d\xc6\x4d\xd3\x8e\x37\xa5\x5e\x5b\x83\x06\x34\x72\xf2\xe7\x04\xdb\x79\xfa\x21\xe1\x1f\x30\x84\x53\x38\x89\x59\xf4\x91\x34\xfd\x12\x9e\x93\xf8\x3f\x91\xac\x5f\x1d\xe0\xfc\x7b\xa6\xec\xbd\x40\xfb\xef\xa7\x72\x53\xf8\xab\x2c\x3b\x85\x5d\x23\x7e\xbb\x67\xc4\x8a\xfe\x12\xf5\x3e\xfd\xff\xed\xd1\x6f\xd3\x3e\xa1\xca\xe4\xf0\x6c\x0f\x22\x21\xe9\x3e\xdb\x89\x83\x68\x5c\x6e\xef\x58\x1a\x8c\x1e\x28\x34\x2f\x9b\x18\x7e\x28\x53\xfe\x47\x85\xe6\x60\x9b\x4a\xcd\x68\xb3\x11\xed\x81\x45\x6f\x25\xae\x68\xd4\x3c\x76\x2c\x92\x1a\x76\xb3\xa6\xf4\xd5\x87\x8f\x18\x24\x6a\x44\x4e\x2e\xb1\xc1\xa7\xfe\x8c\x7b\x5e\x6a\xd2\xe3\xa8\xc6\x10\x13\xdc\x87\x5b\x84\xa5\xd8\xd0\xa8\x96\x15\xfa\x76\x03\x73\xe1\x20\xdd\x68\xb1\x94\x89\x0b\xf2\xb8\xb9\xb7\x38\x17\x96\xc5\x5a\xfc\xad\x40\x47\x73\x1f\x01\x59\x24\xbe\x10\x4a\x6d\x60\x2e\x69\x78\x23\xee\xce\xcb\x57\xc3\x21\x38\x2f\x73\xd4\x69\x0f\xbe\x7b\x35\xf8\xee\x5b\xb0\x85\xc2\x6e\xbf\x5d\x2b\x61\xd5\x51\xa3\x37\x68\x21\xa2\xe7\x35\xe6\x7e\xd1\xe9\xc2\x0f\x0f\xd4\xc2\x07\x0a\xdb\x41\x5a\x78\x01\x27\x5f\xfa\xa4\xd7\xa8\x81\xdb\xe0\x49\x40\xe5\x30\x4a\xa3\x81\xf7\xea\xf5\x55\xe7\x56\x58\xa1\xc4\x0c\xbb\xa7\x3c\x00\xb3\xad\xd6\x22\x4e\x40\xe4\x14\xc8\x95\x90\x1a\x44\x92\x98\x42\x7b\x32\x7c\x39\xcc\xa8\x0d\xe5\xf7\x63\x5f\xca\xe3\x59\x51\x24\x09\x3a\x57\xa6\x7b\xf6\x1a\xa9\x23\x96\xc4\x0d\x52\x3b\x99\x62\xcd\x2b\x94\x1d\x0c\xa7\xe6\x48\x41\xa3\x74\x29\x70\x69\x1c\x6d\x32\x43\x58\x5b\x1a\xbc\x9c\xd4\x09\xdf\x3c\xa4\x48\xd6\x76\x60\x34\x08\x50\x86\xaf\x3b\x38\xc6\x41\xd8\xb9\xeb\x87\x7c\x4f\xdb\x52\xce\xd1\x66\xdd\x6f\x02\xb9\x0e\x55\x1e\x71\x76\x5a\x21\x0d\x78\x27\x9d\xe7\x8e\x9a\xb4\x94\x0e\x02\x92\xa5\x9e\xf7\x20\x37\x39\xe7\xe9\xaf\x95\xb3\x98\xac\xaf\x27\xbf\x4c\xae\xab\xc6\xe7\xe9\x4e\x2c\x67\x9e\xa3\x6a\x24\x04\x4b\xf3\x96\xc7\xf4\xe8\xc0\x10\x73\x00\x50\xa3\x07\x00\x45\xf2\xb7\xb5\xf1\x7d\xed\x38\x4a\x38\xbf\x75\xcc\x1c\xc3\x3c\x57\x57\xc0\x15\xca\xbb\x9d\xdc\xbd\x9b\x1c\x4c\x5e\x56\x08\x52\x8a\xd3\x0e\x25\xf6\xdd\x49\xa3\xb1\xb0\x1d\x38\xb6\xf8\xbc\xa8\xd9\x78\xcd\xed\x66\x20\xaa\xa5\x06\x5e\x2f\xfb\x56\x11\xaa\x01\xeb\x6e\x0a\x4f\x70\xa0\xfa\xbd\x4d\x7e\x73\xe1\x3e\x38\xf6\x7a\x4c\x7f\x33\x39\xbf\xd0\xbe\x53\x2e\x5e\x68\x78\x01\xe5\x03\x25\x75\x78\xd1\x88\xa2\x03\xd9\xb1\x95\xa2\x42\x8f\xb0\x15\x71\x06\x3b\xaf\x48\x50\x30\x07\x1b\xcd\xa2\xdf\x2f\xce\xc3\x28\x8d\x0c\xf6\xcc\xa2\xef\xe3\x6f\x85\x50\xae\x33\xac\x9a\x85\x70\x02\x6f\xb8\xbc\x8d\xf6\x3a\x49\xe2\x69\xf6\x8e\x67\x35\xb6\x68\x8d\x92\x2d\x74\x82\xe7\x26\xc5\x47\x25\x44\x11\x31\x6d\x54\xbe\x8c\xc0\x3c\xd4\x7b\xb7\xea\x04\x70\x54\x35\x04\x99\x90\xaa\xb0\x78\x74\x06\x07\xd2\x8e\x2b\x6c\x26\x12\xf6\xa5\x43\xe0\x69\xdd\x81\x33\x4b\x5c\x98\x75\x50\xe0\x50\xf2\xda\x07\x47\x85\x83\x9d\xf2\xc1\xd7\x4e\xc2\x41\xe1\xc4\x1c\x6b\xe0\xa8\x0c\x5e\x3a\xea\xe0\x15\xc2\x9f\x86\xce\xf3\xea\xf1\x09\x28\xba\xff\x6b\xe0\xb1\xe3\xe7\xbd\x3e\xa7\x24\xe2\x6e\xa7\xf6\x50\x2a\x1b\x9a\x91\xbf\x97\xe3\x9f\x1c\x61\xbb\xb4\xe1\x68\x4d\xe2\x70\xc0\x6d\x5f\xf3\x75\xf7\x57\xab\x0f\x79\xfe\xa1\x96\x89\x30\xaa\x7f\xc5\xc4\x6f\x71\xca\x5d\x0e\x3d\xe5\x16\x57\xd2\x14\x54\xc0\xf0\x7f\x69\x1c\xae\x5a\xbe\xfb\x76\xeb\x3e\xde\x0b\xb2\xdf\xea\x17\x83\xeb\x45\xbc\xd7\x0e\xdd\x52\xad\x7c\x18\xae\xad\xf1\xba\x30\x0b\x37\xce\x2d\xe6\x7f\xe4\x82\x30\x06\xba\x37\x39\xb5\x03\xb1\x3a\x29\x8b\x22\xdd\x54\x05\xb1\x17\x1a\x11\x58\x08\x9d\xc6\x61\x44\xa4\xa9\x24\x79\x0c\x42\xd2\x50\xcc\x85\xd4\xed\x83\x66\xfc\x6a\x15\x3e\x84\x8c\xbd\xde\xb6\x5e\x48\xe3\x10\x49\x13\x1f\x6b\xdc\x7e\x42\xc1\xdc\x09\xa2\xdd\xbb\xce\x78\x5d\x6a\xb4\x2b\x96\xdc\x09\x83\x58\x09\xa9\x04\x4d\x5f\xdc\x61\xe9\x14\x12\x85\x42\x87\x2f\x1c\x98\x79\xb3\x42\xeb\xda\x4f\x00\xf9\x9f\xc1\xf8\x4e\x56\x2c\x1f\xa3\x39\x9e\x1e\xb3\x4f\x8d\xd8\x70\xfc\x37\x4a\x78\x1f\xe1\x55\x33\x6f\x88\x2c\xe9\xf9\xe3\x17\x6a\xdf\x7e\x5a\x48\x71\xcf\x44\x34\x3f\xc0\xb0\xd6\x97\xff\x5d\x82\x6c\x1f\x62\x97\x55\x7f\x16\x0f\xef\x8d\xe9\x81\x42\xc1\x53\x52\xf9\x69\xaa\xec\x47\x1f\x1b\xda\xca\xe8\x0d\x1d\xdd\x5e\xf8\xf2\x9d\xde\x02\xcb\x1b\x90\xd0\xda\xcf\x10\x35\x48\x8f\x56\xd0\x3c\x44\xe8\x8a\x5f\x53\x48\x4b\xc7\xe2\xd8\x2f\x92\x82\x2e\x0a\x8e\x9f\x36\xa8\x30\x4b\x3d\xef\xb7\x5b\xe1\x7d\x2d\xde\x13\x7f\xb7\x8d\xf7\x50\x01\x99\x33\xde\x09\x54\x57\x02\x89\xbf\xe3\x6e\x91\xc7\xe6\x9d\x7b\x01\x5a\xa3\x57\x61\xa6\xde\xb9\x05\x60\xc6\x78\x13\xb0\x7b\x27\x46\x6b\xfc\xae\x01\x70\x26\x9d\x0b\x17\xc4\xec\x84\x84\xbf\xdb\x8f\x88\x92\x81\x82\xe1\xf4\x30\x03\x2d\x1d\x60\xda\xb9\x99\x20\x62\x7e\x15\x56\x43\x3d\x3f\xad\xaf\x86\x57\xf1\xa0\x72\x59\xb3\x8d\x5c\xb2\x6d\xee\xcf\x0e\x27\xb9\x61\x89\xc7\xc3\xc9\x8c\x6c\x5e\x01\xf6\x01\xd6\xfa\xac\xb1\x4f\xf2\x58\xaa\x64\xe9\x65\x66\x7b\x80\x95\xa5\xd7\x5a\x0e\x7f\xf7\x74\x91\x15\x71\x5d\xc5\x06\x4d\x43\x08\xdf\x36\xee\x2d\x1f\x9a\xb4\x68\x50\x89\x84\x65\x73\x35\x1a\x1d\x0d\xef\xaa\x0f\x23\x31\x57\x35\x68\x4a\x25\x42\x64\x84\xf3\x72\x54\xc8\x7f\x62\xdc\xb6\x1e\x83\xe5\x12\x58\x0c\x1f\x70\xb8\x9b\xa5\x10\x34\x33\x6e\x20\x0a\x47\xa3\xe8\x36\xb6\x52\x74\xd2\x62\x0a\x99\x44\x95\x82\x49\xd1\xf2\xa0\xfb\xab\x33\x3a\x7c\xaa\x43\x2b\x49\x62\xf8\x24\x19\xfe\x1d\xc0\x1f\x4a\xb5\x4c\xd0\x6f\x20\x43\xc1\xdf\xdc\xbc\x81\x5c\x38\x07\x4b\x14\x34\xda\x66\x85\x52\x1b\x30\x36\x45\x12\x5e\xcd\x7a\x14\xd6\x06\x0a\x87\xd6\xc1\x7a\x61\x62\xa9\xe5\x16\x2f\xa7\x6e\x55\xfa\x5e\xbc\xce\x91\x2e\x57\x62\x03\xd2\x53\x59\x8f\x87\xaa\x47\x7a\xf5\xa1\x8b\xbf\x96\x19\x32\xf0\x7e\x98\x97\x53\x61\x33\xce\xf9\x35\x3d\x35\x23\x3c\x0e\x45\xcd\xd8\xde\x5e\x74\x35\x03\xb9\x2c\x3d\xcd\x68\xad\x17\xb2\x66\x48\xf2\x0a\x3f\x35\x83\xb1\xd6\x6a\xf3\x02\x23\xa8\x62\xe0\xa7\x9d\xf0\x64\x2d\x63\x7c\x86\xcf\xba\x15\x39\x3f\xf5\x22\x60\xc8\x8b\x1d\x32\xce\x2d\x6e\x28\x9b\x07\x1b\xd5\x4a\x53\x78\xf1\xf9\x16\x37\x5f\x0e\x57\xa2\x08\xc7\x1a\x5d\x55\x7a\xca\xb0\x08\x6b\x8f\x24\x83\x4a\x0b\x39\x1a\x9e\x81\xfc\xbe\xce\x50\x56\x4f\x90\xcf\x9f\x97\x7b\xd6\xd7\x3f\xcb\x2f\x65\x84\x57\x88\xdf\x59\xef\x36\x34\x8a\x31\x12\x68\x28\x28\xda\xf7\xed\x7f\x05\x00\x00\xff\xff\xfb\x65\x93\x4f\xfc\x22\x00\x00")
+var _call_tracerJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x8c\x56\x5f\x6f\xdb\x38\x0c\x7f\x8e\x3f\x05\xaf\x0f\x4b\x82\x65\x71\xbb\x03\xf6\xd0\x2d\x03\x72\x45\xbb\x05\xe8\xb5\x45\x9a\xde\x50\x14\x7d\x50\x6c\xda\xd6\xa6\x48\x86\x44\x37\xcd\x6d\xfd\xee\x07\x4a\x76\x6a\x67\x59\x6f\x2f\x06\x2c\x92\x3f\xfe\xfb\x51\x54\x1c\xc3\x89\x29\x37\x56\xe6\x05\xc1\xdb\xc3\xb7\x47\xb0\x28\x10\x72\xf3\x06\xa9\x40\x8b\xd5\x0a\xa6\x15\x15\xc6\xba\x28\x8e\x61\x51\x48\x07\x99\x54\x08\xd2\x41\x29\x2c\x81\xc9\x80\x76\xf4\x95\x5c\x5a\x61\x37\xe3\x28\x8e\x83\xcd\x5e\x31\x23\x64\x16\x11\x9c\xc9\x68\x2d\x2c\x1e\xc3\xc6\x54\x90\x08\x0d\x16\x53\xe9\xc8\xca\x65\x45\x08\x92\x40\xe8\x34\x36\x16\x56\x26\x95\xd9\x86\x21\x25\x41\xa5\x53\xb4\xde\x35\xa1\x5d\xb9\x26\x8e\x4f\x17\x37\x70\x8e\xce\xa1\x85\x4f\xa8\xd1\x0a\x05\x57\xd5\x52\xc9\x04\xce\x65\x82\xda\x21\x08\x07\x25\x9f\xb8\x02\x53\x58\x7a\x38\x36\x3c\xe3\x50\xae\xeb\x50\xe0\xcc\x54\x3a\x15\x24\x8d\x1e\x01\x4a\x8e\x1c\x1e\xd0\x3a\x69\x34\xfc\xd9\xb8\xaa\x01\x47\x60\x2c\x83\x0c\x04\x71\x02\x16\x4c\xc9\x76\x43\x10\x7a\x03\x4a\xd0\xb3\xe9\x6f\x14\xe4\x39\xef\x14\xa4\xf6\x6e\x0a\x53\x22\x50\x21\x88\xb3\x5e\x4b\xa5\x60\x89\x50\x39\xcc\x2a\x35\x62\xb4\x65\x45\xf0\x65\xb6\xf8\x7c\x79\xb3\x80\xe9\xc5\x2d\x7c\x99\xce\xe7\xd3\x8b\xc5\xed\x7b\x58\x4b\x2a\x4c\x45\x80\x0f\x18\xa0\xe4\xaa\x54\x12\x53\x58\x0b\x6b\x85\xa6\x0d\x98\x8c\x11\xfe\x3e\x9d\x9f\x7c\x9e\x5e\x2c\xa6\x7f\xcd\xce\x67\x8b\x5b\x30\x16\xce\x66\x8b\x8b\xd3\xeb\x6b\x38\xbb\x9c\xc3\x14\xae\xa6\xf3\xc5\xec\xe4\xe6\x7c\x3a\x87\xab\x9b\xf9\xd5\xe5\xf5\xe9\x18\xae\x91\xa3\x42\xb6\xff\xff\x9a\x67\xbe\x7b\x16\x21\x45\x12\x52\xb9\xa6\x12\xb7\xa6\x02\x57\x98\x4a\xa5\x50\x88\x07\x04\x8b\x09\xca\x07\x4c\x41\x40\x62\xca\xcd\x6f\x37\x95\xb1\x84\x32\x3a\xf7\x39\xff\x92\x90\x30\xcb\x40\x1b\x1a\x81\x43\x84\x0f\x05\x51\x79\x1c\xc7\xeb\xf5\x7a\x9c\xeb\x6a\x6c\x6c\x1e\xab\x00\xe7\xe2\x8f\xe3\x28\x62\xd0\x44\x28\x75\x66\xc5\x0a\x17\x56\x24\x68\xb9\xee\xce\xc3\x6b\x5c\x7b\x21\x64\x2c\x05\xb2\x22\x91\x3a\x87\x15\x52\x61\x52\x07\x64\xc0\x62\x69\x2c\xd5\x9d\x02\xa9\x33\x63\x57\x9e\x51\x3e\xd8\x25\x37\x46\x6a\x42\xab\x85\x82\x15\x3a\x27\x72\xf4\x2c\x16\x0c\xa6\x9d\x48\xc8\x53\xe6\x7b\xd4\x63\x3f\x8e\x44\xf2\xed\x18\xee\xbe\x3f\xdd\x8f\xa2\x5e\x26\x2a\x45\xc7\x90\x55\xda\x6b\x0d\x94\xc9\x47\x90\x2e\x87\xf0\xfd\x69\x14\xf5\x2c\xba\xae\x38\xa1\xc7\x5a\x1c\xf5\x7a\x71\x0c\x57\x16\x4b\x66\xb9\xa9\x98\x9d\xb5\x73\x1f\x62\xd4\xeb\x3d\x08\x0b\x01\x01\x26\xde\xa0\x47\x9b\x12\x8f\x01\x00\x12\x7a\x1c\xf3\xcf\x88\x4f\x33\x6b\x56\xfe\x94\xcc\x67\x7c\x64\x1f\x63\x3e\x1a\x7a\x21\x19\x2f\x6a\x0b\xc9\x04\xd1\x83\x50\x95\x87\xeb\x1f\x3e\xf6\xe1\xb5\x07\xf5\x67\x63\x32\xd7\x64\xa5\xce\x07\x47\xef\x82\x6a\x2e\x5c\x80\xa9\x55\x97\x32\x9f\x69\xf2\x68\xb9\x70\xc3\xbd\x06\x37\x0e\xd3\xe3\xfd\x06\x2c\xda\x63\x24\x75\x59\xd1\x71\x27\x56\x7f\x14\xa4\xa6\xa2\x20\x7e\x96\x86\x23\x2f\x7e\x8a\x7a\x3d\x99\xc1\x80\x0a\xe9\xc6\xdb\x3e\xdd\x1d\xde\x87\x1f\xf8\x63\x32\xf1\x37\x55\x26\x35\xa6\xa1\xfe\x75\x7b\x6a\x85\x09\xfc\xc2\xf4\x45\x70\xb4\xd6\xd8\x97\xc0\x83\xc2\x3e\x70\x2f\x61\x70\x40\xe5\x10\x18\x9f\x73\xfa\x6d\xc4\xad\x72\x2b\xc0\x8e\x4a\x07\x03\x5e\xbd\xda\x23\x3e\xc0\x47\x4c\x2a\xa6\x26\x58\x7c\x40\x4b\x98\x1e\xc0\x8f\x1f\x35\xed\xea\xfa\xc2\x64\x32\x39\x38\x7c\x3c\x18\xd6\x71\xa4\xa8\x90\xb0\xab\xe3\x63\x88\x38\x46\xaa\xac\x0e\xd9\x66\x52\x0b\x25\xff\xc5\xda\xed\x30\xea\xf1\x4c\x20\x8f\x5a\x6b\x24\xfc\xd8\x06\x64\x26\xbc\x1f\xe5\x0e\xdd\xbd\xc2\x38\x47\x5a\x6c\x4a\x1c\x0c\x5b\x94\x0f\x44\xd8\xca\xcf\xac\x59\x0d\x86\xcf\xb4\xdf\x11\x2f\x4c\x23\xac\x79\xb6\x23\x9f\xf1\x69\xa3\xe2\x09\xdf\xe5\xee\x56\xf1\x93\x70\x83\x61\x8b\xbe\xfd\xa3\x77\xfd\x0e\x07\xb7\x9a\xff\xf0\x34\x0d\x86\x3b\xdd\xf4\xb9\x71\x9e\x61\xda\x26\xbf\x70\x53\x1b\x77\xe7\xa4\xf6\xd2\x65\xd3\xb8\xac\x5c\x31\xe0\xdf\xa6\xc6\x8f\x92\x76\x4b\x3c\x0f\x4d\xd8\x16\x5a\xa1\xfe\x89\x96\x63\x85\x3a\xa7\xa2\x4e\x83\x35\x3e\xc2\x51\xdd\xf5\x56\x73\x76\xbd\x9b\x72\x30\xdc\xe6\x54\x8f\x37\x4c\xf6\x95\x2f\x04\x51\x17\x91\xd5\x7e\x2e\x64\xe3\xab\xa1\xf9\x8e\xdd\x29\x1f\x07\x77\x1c\x63\xad\xb5\x67\x5a\x42\x34\x0d\x83\xdb\xcd\x7e\x06\xbb\xf4\xd2\xc1\xd0\xc3\xd5\x73\xd8\x32\x6e\x42\x68\xa6\x2c\xb8\xf4\x22\xa6\xa6\x77\xdb\x3f\x99\x9f\x4e\x17\xa7\x7d\x9e\x9a\xbd\x92\xb7\xfd\x26\xa0\x66\x70\x82\x9a\xf1\x67\x4f\x51\xf3\xe1\x6a\xbf\x99\xc0\x51\x93\xd9\xce\x85\xa1\x50\xbf\x39\x6a\x2e\xb3\xbd\xf9\xbe\x68\x00\x77\xf7\x5b\x4f\x2f\x28\x76\x98\xc4\xda\xcc\xa6\x38\x86\x66\x94\xf9\x5d\x60\x51\x10\x3a\x7e\x18\x30\x1b\xcc\xf2\x2b\x26\xbc\x5c\x79\xe9\xf2\x3e\xf6\xaa\x90\xa2\x93\x16\x53\xc8\x24\xaa\x14\x0c\xbf\x10\xf9\xe9\xf1\xd5\x19\xed\x01\x1d\x5a\xc9\x88\x7e\x0f\x8f\xc3\x6b\x56\x32\xa8\x96\x09\xd2\x06\x32\x14\x54\x59\xe4\xf5\x5d\x0a\xe7\x60\x85\x42\x4b\x9d\x67\x95\x52\x1b\x30\x36\x45\x06\x0f\xf7\x8a\xf3\x80\x64\x78\xc1\x5b\x07\xeb\xc2\x40\x6a\x74\xbf\x5e\xea\xa5\x45\x7e\xaf\x8d\xe0\x6b\xe5\x88\x5f\x75\xa5\x12\x1b\x90\x34\x8e\x7a\x4d\x52\xed\xfd\xcc\x99\x6f\x47\xc4\x19\xbe\x10\x7f\x5e\xbe\x4d\x9b\xbb\xdb\xd7\x1f\xf3\x5f\x77\xef\xd6\xdd\xee\x6e\xdc\xe7\xe9\xef\xae\xd7\x66\x82\xba\x3b\xb4\x3d\x57\xdd\x45\xe9\x25\xfe\xaf\xbb\x22\x5b\xdc\xf7\x02\xcf\xe0\xad\x81\xff\x0b\x51\xca\x55\x3b\x27\xb9\x0a\xf1\x78\x2e\x6c\xd5\xfd\x5f\x73\xbf\x71\x17\x07\x5c\x9c\x6f\xb8\xe1\x87\x71\xa8\x51\xcd\x41\xe6\x6d\x38\xb8\xfb\x86\x9b\xfb\xfd\x3c\xad\xa7\xa0\xa5\xd7\x30\xb3\xb9\x3f\x83\xe8\x85\xc5\xbd\x0d\x42\x4e\x0e\xdf\x83\xfc\xd0\x36\xa8\xef\xb0\xf7\x20\x5f\xbf\x6e\x5c\xb6\xe5\x77\xf2\xbe\xb9\xc2\xb6\x0b\x6a\x47\x3e\x6c\x07\x54\x6f\xb4\xa0\x12\xf5\x9e\xa2\xa7\xe8\xbf\x00\x00\x00\xff\xff\x2a\xac\x9f\xff\xa9\x0d\x00\x00")
func call_tracerJsBytes() ([]byte, error) {
return bindataRead(
@@ -133,6 +155,26 @@ func call_tracerJs() (*asset, error) {
}
info := bindataFileInfo{name: "call_tracer.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
+ a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x42, 0x13, 0x7a, 0x14, 0xbf, 0xa7, 0x49, 0x4f, 0xb4, 0x4f, 0x45, 0x1, 0xbc, 0x9e, 0xd1, 0x8e, 0xc7, 0xee, 0x61, 0xfa, 0x82, 0x52, 0xa4, 0x78, 0xfe, 0xff, 0xb1, 0x68, 0x1d, 0xcc, 0x1d, 0x8e}}
+ return a, nil
+}
+
+var _call_tracer_legacyJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\xd4\x5a\xdf\x6f\x1b\x37\xf2\x7f\x96\xfe\x8a\x89\x1f\x6a\x09\x51\x24\x39\xe9\xb7\x5f\xc0\xae\x7a\x50\x1d\x25\x35\xe0\xc6\x81\xad\x34\x08\x82\x3c\x50\xbb\xb3\x12\x6b\x8a\xdc\x92\x5c\xc9\xba\xd6\xff\xfb\x61\x86\xdc\xd5\xae\x24\x3b\xbe\x5e\x71\xe8\xbd\x69\x97\x33\xc3\xe1\xcc\x67\x7e\x71\x35\x18\xc0\xb9\xc9\x37\x56\xce\x17\x1e\x5e\x0e\x4f\xfe\x1f\xa6\x0b\x84\xb9\x79\x81\x7e\x81\x16\x8b\x25\x8c\x0b\xbf\x30\xd6\xb5\x07\x03\x98\x2e\xa4\x83\x4c\x2a\x04\xe9\x20\x17\xd6\x83\xc9\xc0\xef\xd0\x2b\x39\xb3\xc2\x6e\xfa\xed\xc1\x20\xf0\x1c\x5c\x26\x09\x99\x45\x04\x67\x32\xbf\x16\x16\x4f\x61\x63\x0a\x48\x84\x06\x8b\xa9\x74\xde\xca\x59\xe1\x11\xa4\x07\xa1\xd3\x81\xb1\xb0\x34\xa9\xcc\x36\x24\x52\x7a\x28\x74\x8a\x96\xb7\xf6\x68\x97\xae\xd4\xe3\xed\xbb\x0f\x70\x89\xce\xa1\x85\xb7\xa8\xd1\x0a\x05\xef\x8b\x99\x92\x09\x5c\xca\x04\xb5\x43\x10\x0e\x72\x7a\xe3\x16\x98\xc2\x8c\xc5\x11\xe3\x1b\x52\xe5\x26\xaa\x02\x6f\x4c\xa1\x53\xe1\xa5\xd1\x3d\x40\x49\x9a\xc3\x0a\xad\x93\x46\xc3\xab\x72\xab\x28\xb0\x07\xc6\x92\x90\x8e\xf0\x74\x00\x0b\x26\x27\xbe\x2e\x08\xbd\x01\x25\xfc\x96\xf5\x09\x06\xd9\x9e\x3b\x05\xa9\x79\x9b\x85\xc9\x11\xfc\x42\x78\x3a\xf5\x5a\x2a\x05\x33\x84\xc2\x61\x56\xa8\x1e\x49\x9b\x15\x1e\x3e\x5e\x4c\x7f\xba\xfa\x30\x85\xf1\xbb\x4f\xf0\x71\x7c\x7d\x3d\x7e\x37\xfd\x74\x06\x6b\xe9\x17\xa6\xf0\x80\x2b\x0c\xa2\xe4\x32\x57\x12\x53\x58\x0b\x6b\x85\xf6\x1b\x30\x19\x49\xf8\x79\x72\x7d\xfe\xd3\xf8\xdd\x74\xfc\xe3\xc5\xe5\xc5\xf4\x13\x18\x0b\x6f\x2e\xa6\xef\x26\x37\x37\xf0\xe6\xea\x1a\xc6\xf0\x7e\x7c\x3d\xbd\x38\xff\x70\x39\xbe\x86\xf7\x1f\xae\xdf\x5f\xdd\x4c\xfa\x70\x83\xa4\x15\x12\xff\xd7\x6d\x9e\xb1\xf7\x2c\x42\x8a\x5e\x48\xe5\x4a\x4b\x7c\x32\x05\xb8\x85\x29\x54\x0a\x0b\xb1\x42\xb0\x98\xa0\x5c\x61\x0a\x02\x12\x93\x6f\x9e\xec\x54\x92\x25\x94\xd1\x73\x3e\xf3\x83\x80\x84\x8b\x0c\xb4\xf1\x3d\x70\x88\xf0\xfd\xc2\xfb\xfc\x74\x30\x58\xaf\xd7\xfd\xb9\x2e\xfa\xc6\xce\x07\x2a\x88\x73\x83\x1f\xfa\x6d\x92\x99\x08\xa5\xa6\x56\x24\x68\xc9\x39\x02\xb2\x82\xcc\xaf\xcc\x5a\x83\xb7\x42\x3b\x91\x90\xab\xe9\x77\xc2\x60\x14\x1e\xf0\x8e\x9e\xbc\x23\xd0\x82\xc5\xdc\x58\xfa\xad\x54\x89\x33\xa9\x3d\x5a\x2d\x14\xcb\x76\xb0\x14\x29\xc2\x6c\x03\xa2\x2e\xb0\x57\x3f\x0c\xc1\x28\xb8\x1b\xa4\xce\x8c\x5d\x32\x2c\xfb\xed\xdf\xdb\xad\xa8\xa1\xf3\x22\xb9\x25\x05\x49\x7e\x52\x58\x8b\xda\x93\x29\x0b\xeb\xe4\x0a\x99\x04\x02\x4d\xb4\xe7\xe4\x97\x9f\x01\xef\x30\x29\x82\xa4\x56\x25\xe4\x14\x3e\xff\x7e\xff\xa5\xd7\x66\xd1\x29\xba\x04\x75\x8a\x29\x9f\xef\xd6\xc1\x7a\xc1\x16\x85\x35\x1e\xaf\x10\x7e\x2d\x9c\xaf\xd1\x64\xd6\x2c\x41\x68\x30\x05\x21\xbe\x6e\x1d\xa9\xbd\x61\x81\x82\x7e\x6b\xb4\xac\x51\xbf\xdd\xaa\x98\x4f\x21\x13\xca\x61\xdc\xd7\x79\xcc\xe9\x34\x52\xaf\xcc\x2d\x49\x36\x96\x20\x6c\x37\x60\xf2\xc4\xa4\x31\x18\xe8\x1c\xd5\x31\xd0\xf5\xdb\x2d\xe2\x3b\x85\xac\xd0\xbc\x6d\x47\x99\x79\x0f\xd2\x59\x17\x7e\x6f\xb7\x48\xec\xb9\xc8\x7d\x61\x91\xed\x89\xd6\x1a\xeb\x40\x2e\x97\x98\x4a\xe1\x51\x6d\xda\xad\xd6\x4a\xd8\xb0\x00\x23\x50\x66\xde\x9f\xa3\x9f\xd0\x63\xa7\x7b\xd6\x6e\xb5\x64\x06\x9d\xb0\xfa\x6c\x34\xe2\xec\x93\x49\x8d\x69\x10\xdf\xf2\x0b\xe9\xfa\x99\x28\x94\xaf\xf6\x25\xa6\x96\x45\x5f\x58\x4d\x3f\xef\x83\x16\x1f\x11\x8c\x56\x1b\x48\x28\xcb\x88\x19\x85\xa7\xdb\x38\x8f\xcb\x78\x38\xd7\x83\x4c\x38\x32\xa1\xcc\x60\x8d\x90\x5b\x7c\x91\x2c\x90\x7c\xa7\x13\x8c\x5a\xba\x8d\x63\xa7\x8e\x80\x76\xeb\x9b\xbc\xef\xcd\xbb\x62\x39\x43\xdb\xe9\xc2\x37\x30\xbc\xcb\x86\x5d\x18\x8d\xf8\x47\xa9\x7b\xe4\x89\xfa\x92\x14\x93\xc7\x83\x32\xff\x8d\xb7\x52\xcf\xc3\x59\xa3\xae\x17\x19\x08\xd0\xb8\x86\xc4\x68\x06\x35\x79\x65\x86\x52\xcf\x21\xb1\x28\x3c\xa6\x3d\x10\x69\x0a\xde\x04\xe4\x55\x38\x6b\x6e\x09\xdf\x7c\x03\x1d\xda\x6c\x04\xc7\xe7\xd7\x93\xf1\x74\x72\x0c\x7f\xfc\x01\xe1\xcd\x51\x78\xf3\xf2\xa8\x5b\xd3\x4c\xea\xab\x2c\x8b\xca\xb1\xc0\x7e\x8e\x78\xdb\x39\xe9\xf6\x57\x42\x15\x78\x95\x05\x35\x23\xed\x44\xa7\x30\x8a\x3c\xcf\x77\x79\x5e\x36\x78\x88\x69\x30\x80\xb1\x73\xb8\x9c\x29\xdc\x0f\xc8\x18\xb1\x1c\xbc\xce\x53\xc6\x22\xf4\x25\x66\x99\x2b\x24\x54\x95\xbb\x46\xf3\xb3\xc6\x2d\xbf\xc9\xf1\x14\x00\xc0\xe4\x3d\x7e\x41\xb1\xc0\x2f\xbc\xf9\x09\xef\xd8\x47\xa5\x09\x09\x55\xe3\x34\xb5\xe8\x5c\xa7\xdb\x0d\xe4\x52\xe7\x85\x3f\x6d\x90\x2f\x71\x69\xec\xa6\xef\x28\x21\x75\xf8\x68\xbd\x70\xd2\x92\x67\x2e\xdc\x85\x26\x9e\x88\xd4\xb7\xc2\x75\xb6\x4b\xe7\xc6\xf9\xd3\x72\x89\x1e\xca\x35\xb6\x05\xb1\x1d\x0f\xef\x8e\xf7\xad\x35\xec\x6e\x91\x70\xf2\x5d\x97\x58\xee\xcf\x2a\x7c\x57\x69\xa2\x9f\x17\x6e\xd1\x61\x38\x6d\x57\xb7\xa9\x60\x04\xde\x16\x78\x10\xfe\x0c\xa9\x7d\x38\x39\x54\x19\xe5\x12\x6f\x8b\x84\x61\x35\x17\x9c\x69\x38\xd2\x05\x65\x5e\x57\xcc\xd8\xe6\xde\x98\x7d\x74\x45\x70\xdd\x4c\x2e\xdf\xbc\x9e\xdc\x4c\xaf\x3f\x9c\x4f\x8f\x6b\x70\x52\x98\x79\x52\xaa\x79\x06\x85\x7a\xee\x17\xac\x3f\x89\x6b\xae\x7e\x26\x9e\x17\x27\x5f\xc2\x1b\x18\x1d\x08\xf9\xd6\xe3\x1c\xf0\xf9\x0b\xcb\xbe\xdf\x37\x5f\x93\x34\x18\xf3\xaf\x41\x92\x37\x4c\x5c\x92\x7b\x53\x12\x3c\xee\xe7\xbf\x18\x54\xe9\x8c\x28\x7e\x14\x4a\xe8\x04\x1f\xd1\x79\x1f\x6b\xf5\xa4\x79\x20\x0f\x2d\xd1\x2f\x4c\xca\x85\x21\x11\xa1\xb6\x94\x08\x4a\x8d\xc6\x7f\x3f\x1b\x8d\x2f\x2f\x6b\xb9\x88\x9f\xcf\xaf\x5e\xd7\xf3\xd3\xf1\xeb\xc9\xe5\xe4\xed\x78\x3a\xd9\xa5\xbd\x99\x8e\xa7\x17\xe7\xfc\xb6\x4c\x5d\x83\x01\xdc\xdc\xca\x9c\x2b\x0c\xe7\x6d\xb3\xcc\xb9\x55\xae\xf4\x75\x3d\xf0\x0b\x43\x4d\xa8\x8d\x05\x34\x13\x3a\x29\x0b\x9b\x2b\x01\xeb\x0d\xc1\xf5\x21\xe7\x9d\xec\x38\xaf\x82\xb0\x74\xef\x2d\xc6\x4d\xd3\x8e\x37\xa5\x5e\x5b\x83\x06\x34\x72\xf2\xe7\x04\xdb\x79\xfa\x21\xe1\x1f\x30\x84\x53\x38\x89\x59\xf4\x91\x34\xfd\x12\x9e\x93\xf8\x3f\x91\xac\x5f\x1d\xe0\xfc\x7b\xa6\xec\xbd\x40\xfb\xef\xa7\x72\x53\xf8\xab\x2c\x3b\x85\x5d\x23\x7e\xbb\x67\xc4\x8a\xfe\x12\xf5\x3e\xfd\xff\xed\xd1\x6f\xd3\x3e\xa1\xca\xe4\xf0\x6c\x0f\x22\x21\xe9\x3e\xdb\x89\x83\x68\x5c\x6e\xef\x58\x1a\x8c\x1e\x28\x34\x2f\x9b\x18\x7e\x28\x53\xfe\x47\x85\xe6\x60\x9b\x4a\xcd\x68\xb3\x11\xed\x81\x45\x6f\x25\xae\x68\xd4\x3c\x76\x2c\x92\x1a\x76\xb3\xa6\xf4\xd5\x87\x8f\x18\x24\x6a\x44\x4e\x2e\xb1\xc1\xa7\xfe\x8c\x7b\x5e\x6a\xd2\xe3\xa8\xc6\x10\x13\xdc\x87\x5b\x84\xa5\xd8\xd0\xa8\x96\x15\xfa\x76\x03\x73\xe1\x20\xdd\x68\xb1\x94\x89\x0b\xf2\xb8\xb9\xb7\x38\x17\x96\xc5\x5a\xfc\xad\x40\x47\x73\x1f\x01\x59\x24\xbe\x10\x4a\x6d\x60\x2e\x69\x78\x23\xee\xce\xcb\x57\xc3\x21\x38\x2f\x73\xd4\x69\x0f\xbe\x7b\x35\xf8\xee\x5b\xb0\x85\xc2\x6e\xbf\x5d\x2b\x61\xd5\x51\xa3\x37\x68\x21\xa2\xe7\x35\xe6\x7e\xd1\xe9\xc2\x0f\x0f\xd4\xc2\x07\x0a\xdb\x41\x5a\x78\x01\x27\x5f\xfa\xa4\xd7\xa8\x81\xdb\xe0\x49\x40\xe5\x30\x4a\xa3\x81\xf7\xea\xf5\x55\xe7\x56\x58\xa1\xc4\x0c\xbb\xa7\x3c\x00\xb3\xad\xd6\x22\x4e\x40\xe4\x14\xc8\x95\x90\x1a\x44\x92\x98\x42\x7b\x32\x7c\x39\xcc\xa8\x0d\xe5\xf7\x63\x5f\xca\xe3\x59\x51\x24\x09\x3a\x57\xa6\x7b\xf6\x1a\xa9\x23\x96\xc4\x0d\x52\x3b\x99\x62\xcd\x2b\x94\x1d\x0c\xa7\xe6\x48\x41\xa3\x74\x29\x70\x69\x1c\x6d\x32\x43\x58\x5b\x1a\xbc\x9c\xd4\x09\xdf\x3c\xa4\x48\xd6\x76\x60\x34\x08\x50\x86\xaf\x3b\x38\xc6\x41\xd8\xb9\xeb\x87\x7c\x4f\xdb\x52\xce\xd1\x66\xdd\x6f\x02\xb9\x0e\x55\x1e\x71\x76\x5a\x21\x0d\x78\x27\x9d\xe7\x8e\x9a\xb4\x94\x0e\x02\x92\xa5\x9e\xf7\x20\x37\x39\xe7\xe9\xaf\x95\xb3\x98\xac\xaf\x27\xbf\x4c\xae\xab\xc6\xe7\xe9\x4e\x2c\x67\x9e\xa3\x6a\x24\x04\x4b\xf3\x96\xc7\xf4\xe8\xc0\x10\x73\x00\x50\xa3\x07\x00\x45\xf2\xb7\xb5\xf1\x7d\xed\x38\x4a\x38\xbf\x75\xcc\x1c\xc3\x3c\x57\x57\xc0\x15\xca\xbb\x9d\xdc\xbd\x9b\x1c\x4c\x5e\x56\x08\x52\x8a\xd3\x0e\x25\xf6\xdd\x49\xa3\xb1\xb0\x1d\x38\xb6\xf8\xbc\xa8\xd9\x78\xcd\xed\x66\x20\xaa\xa5\x06\x5e\x2f\xfb\x56\x11\xaa\x01\xeb\x6e\x0a\x4f\x70\xa0\xfa\xbd\x4d\x7e\x73\xe1\x3e\x38\xf6\x7a\x4c\x7f\x33\x39\xbf\xd0\xbe\x53\x2e\x5e\x68\x78\x01\xe5\x03\x25\x75\x78\xd1\x88\xa2\x03\xd9\xb1\x95\xa2\x42\x8f\xb0\x15\x71\x06\x3b\xaf\x48\x50\x30\x07\x1b\xcd\xa2\xdf\x2f\xce\xc3\x28\x8d\x0c\xf6\xcc\xa2\xef\xe3\x6f\x85\x50\xae\x33\xac\x9a\x85\x70\x02\x6f\xb8\xbc\x8d\xf6\x3a\x49\xe2\x69\xf6\x8e\x67\x35\xb6\x68\x8d\x92\x2d\x74\x82\xe7\x26\xc5\x47\x25\x44\x11\x31\x6d\x54\xbe\x8c\xc0\x3c\xd4\x7b\xb7\xea\x04\x70\x54\x35\x04\x99\x90\xaa\xb0\x78\x74\x06\x07\xd2\x8e\x2b\x6c\x26\x12\xf6\xa5\x43\xe0\x69\xdd\x81\x33\x4b\x5c\x98\x75\x50\xe0\x50\xf2\xda\x07\x47\x85\x83\x9d\xf2\xc1\xd7\x4e\xc2\x41\xe1\xc4\x1c\x6b\xe0\xa8\x0c\x5e\x3a\xea\xe0\x15\xc2\x9f\x86\xce\xf3\xea\xf1\x09\x28\xba\xff\x6b\xe0\xb1\xe3\xe7\xbd\x3e\xa7\x24\xe2\x6e\xa7\xf6\x50\x2a\x1b\x9a\x91\xbf\x97\xe3\x9f\x1c\x61\xbb\xb4\xe1\x68\x4d\xe2\x70\xc0\x6d\x5f\xf3\x75\xf7\x57\xab\x0f\x79\xfe\xa1\x96\x89\x30\xaa\x7f\xc5\xc4\x6f\x71\xca\x5d\x0e\x3d\xe5\x16\x57\xd2\x14\x54\xc0\xf0\x7f\x69\x1c\xae\x5a\xbe\xfb\x76\xeb\x3e\xde\x0b\xb2\xdf\xea\x17\x83\xeb\x45\xbc\xd7\x0e\xdd\x52\xad\x7c\x18\xae\xad\xf1\xba\x30\x0b\x37\xce\x2d\xe6\x7f\xe4\x82\x30\x06\xba\x37\x39\xb5\x03\xb1\x3a\x29\x8b\x22\xdd\x54\x05\xb1\x17\x1a\x11\x58\x08\x9d\xc6\x61\x44\xa4\xa9\x24\x79\x0c\x42\xd2\x50\xcc\x85\xd4\xed\x83\x66\xfc\x6a\x15\x3e\x84\x8c\xbd\xde\xb6\x5e\x48\xe3\x10\x49\x13\x1f\x6b\xdc\x7e\x42\xc1\xdc\x09\xa2\xdd\xbb\xce\x78\x5d\x6a\xb4\x2b\x96\xdc\x09\x83\x58\x09\xa9\x04\x4d\x5f\xdc\x61\xe9\x14\x12\x85\x42\x87\x2f\x1c\x98\x79\xb3\x42\xeb\xda\x4f\x00\xf9\x9f\xc1\xf8\x4e\x56\x2c\x1f\xa3\x39\x9e\x1e\xb3\x4f\x8d\xd8\x70\xfc\x37\x4a\x78\x1f\xe1\x55\x33\x6f\x88\x2c\xe9\xf9\xe3\x17\x6a\xdf\x7e\x5a\x48\x71\xcf\x44\x34\x3f\xc0\xb0\xd6\x97\xff\x5d\x82\x6c\x1f\x62\x97\x55\x7f\x16\x0f\xef\x8d\xe9\x81\x42\xc1\x53\x52\xf9\x69\xaa\xec\x47\x1f\x1b\xda\xca\xe8\x0d\x1d\xdd\x5e\xf8\xf2\x9d\xde\x02\xcb\x1b\x90\xd0\xda\xcf\x10\x35\x48\x8f\x56\xd0\x3c\x44\xe8\x8a\x5f\x53\x48\x4b\xc7\xe2\xd8\x2f\x92\x82\x2e\x0a\x8e\x9f\x36\xa8\x30\x4b\x3d\xef\xb7\x5b\xe1\x7d\x2d\xde\x13\x7f\xb7\x8d\xf7\x50\x01\x99\x33\xde\x09\x54\x57\x02\x89\xbf\xe3\x6e\x91\xc7\xe6\x9d\x7b\x01\x5a\xa3\x57\x61\xa6\xde\xb9\x05\x60\xc6\x78\x13\xb0\x7b\x27\x46\x6b\xfc\xae\x01\x70\x26\x9d\x0b\x17\xc4\xec\x84\x84\xbf\xdb\x8f\x88\x92\x81\x82\xe1\xf4\x30\x03\x2d\x1d\x60\xda\xb9\x99\x20\x62\x7e\x15\x56\x43\x3d\x3f\xad\xaf\x86\x57\xf1\xa0\x72\x59\xb3\x8d\x5c\xb2\x6d\xee\xcf\x0e\x27\xb9\x61\x89\xc7\xc3\xc9\x8c\x6c\x5e\x01\xf6\x01\xd6\xfa\xac\xb1\x4f\xf2\x58\xaa\x64\xe9\x65\x66\x7b\x80\x95\xa5\xd7\x5a\x0e\x7f\xf7\x74\x91\x15\x71\x5d\xc5\x06\x4d\x43\x08\xdf\x36\xee\x2d\x1f\x9a\xb4\x68\x50\x89\x84\x65\x73\x35\x1a\x1d\x0d\xef\xaa\x0f\x23\x31\x57\x35\x68\x4a\x25\x42\x64\x84\xf3\x72\x54\xc8\x7f\x62\xdc\xb6\x1e\x83\xe5\x12\x58\x0c\x1f\x70\xb8\x9b\xa5\x10\x34\x33\x6e\x20\x0a\x47\xa3\xe8\x36\xb6\x52\x74\xd2\x62\x0a\x99\x44\x95\x82\x49\xd1\xf2\xa0\xfb\xab\x33\x3a\x7c\xaa\x43\x2b\x49\x62\xf8\x24\x19\xfe\x1d\xc0\x1f\x4a\xb5\x4c\xd0\x6f\x20\x43\xc1\xdf\xdc\xbc\x81\x5c\x38\x07\x4b\x14\x34\xda\x66\x85\x52\x1b\x30\x36\x45\x12\x5e\xcd\x7a\x14\xd6\x06\x0a\x87\xd6\xc1\x7a\x61\x62\xa9\xe5\x16\x2f\xa7\x6e\x55\xfa\x5e\xbc\xce\x91\x2e\x57\x62\x03\xd2\x53\x59\x8f\x87\xaa\x47\x7a\xf5\xa1\x8b\xbf\x96\x19\x32\xf0\x7e\x98\x97\x53\x61\x33\xce\xf9\x35\x3d\x35\x23\x3c\x0e\x45\xcd\xd8\xde\x5e\x74\x35\x03\xb9\x2c\x3d\xcd\x68\xad\x17\xb2\x66\x48\xf2\x0a\x3f\x35\x83\xb1\xd6\x6a\xf3\x02\x23\xa8\x62\xe0\xa7\x9d\xf0\x64\x2d\x63\x7c\x86\xcf\xba\x15\x39\x3f\xf5\x22\x60\xc8\x8b\x1d\x32\xce\x2d\x6e\x28\x9b\x07\x1b\xd5\x4a\x53\x78\xf1\xf9\x16\x37\x5f\x0e\x57\xa2\x08\xc7\x1a\x5d\x55\x7a\xca\xb0\x08\x6b\x8f\x24\x83\x4a\x0b\x39\x1a\x9e\x81\xfc\xbe\xce\x50\x56\x4f\x90\xcf\x9f\x97\x7b\xd6\xd7\x3f\xcb\x2f\x65\x84\x57\x88\xdf\x59\xef\x36\x34\x8a\x31\x12\x68\x28\x28\xda\xf7\xed\x7f\x05\x00\x00\xff\xff\xfb\x65\x93\x4f\xfc\x22\x00\x00")
+
+func call_tracer_legacyJsBytes() ([]byte, error) {
+ return bindataRead(
+ _call_tracer_legacyJs,
+ "call_tracer_legacy.js",
+ )
+}
+
+func call_tracer_legacyJs() (*asset, error) {
+ bytes, err := call_tracer_legacyJsBytes()
+ if err != nil {
+ return nil, err
+ }
+
+ info := bindataFileInfo{name: "call_tracer_legacy.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x46, 0x79, 0xb6, 0xbc, 0xd2, 0xc, 0x25, 0xb1, 0x22, 0x56, 0xef, 0x77, 0xb9, 0x5e, 0x2e, 0xf4, 0xda, 0xb2, 0x2f, 0x53, 0xa4, 0xff, 0xc8, 0xac, 0xbb, 0x75, 0x22, 0x46, 0x59, 0xe3, 0x1d, 0x7d}}
return a, nil
}
@@ -348,15 +390,17 @@ func AssetNames() []string {
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
- "4byte_tracer.js": _4byte_tracerJs,
- "bigram_tracer.js": bigram_tracerJs,
- "call_tracer.js": call_tracerJs,
- "evmdis_tracer.js": evmdis_tracerJs,
- "noop_tracer.js": noop_tracerJs,
- "opcount_tracer.js": opcount_tracerJs,
- "prestate_tracer.js": prestate_tracerJs,
- "trigram_tracer.js": trigram_tracerJs,
- "unigram_tracer.js": unigram_tracerJs,
+ "4byte_tracer.js": _4byte_tracerJs,
+ "4byte_tracer_legacy.js": _4byte_tracer_legacyJs,
+ "bigram_tracer.js": bigram_tracerJs,
+ "call_tracer.js": call_tracerJs,
+ "call_tracer_legacy.js": call_tracer_legacyJs,
+ "evmdis_tracer.js": evmdis_tracerJs,
+ "noop_tracer.js": noop_tracerJs,
+ "opcount_tracer.js": opcount_tracerJs,
+ "prestate_tracer.js": prestate_tracerJs,
+ "trigram_tracer.js": trigram_tracerJs,
+ "unigram_tracer.js": unigram_tracerJs,
}
// AssetDebug is true if the assets were built with the debug flag enabled.
@@ -403,15 +447,17 @@ type bintree struct {
}
var _bintree = &bintree{nil, map[string]*bintree{
- "4byte_tracer.js": {_4byte_tracerJs, map[string]*bintree{}},
- "bigram_tracer.js": {bigram_tracerJs, map[string]*bintree{}},
- "call_tracer.js": {call_tracerJs, map[string]*bintree{}},
- "evmdis_tracer.js": {evmdis_tracerJs, map[string]*bintree{}},
- "noop_tracer.js": {noop_tracerJs, map[string]*bintree{}},
- "opcount_tracer.js": {opcount_tracerJs, map[string]*bintree{}},
- "prestate_tracer.js": {prestate_tracerJs, map[string]*bintree{}},
- "trigram_tracer.js": {trigram_tracerJs, map[string]*bintree{}},
- "unigram_tracer.js": {unigram_tracerJs, map[string]*bintree{}},
+ "4byte_tracer.js": {_4byte_tracerJs, map[string]*bintree{}},
+ "4byte_tracer_legacy.js": {_4byte_tracer_legacyJs, map[string]*bintree{}},
+ "bigram_tracer.js": {bigram_tracerJs, map[string]*bintree{}},
+ "call_tracer.js": {call_tracerJs, map[string]*bintree{}},
+ "call_tracer_legacy.js": {call_tracer_legacyJs, map[string]*bintree{}},
+ "evmdis_tracer.js": {evmdis_tracerJs, map[string]*bintree{}},
+ "noop_tracer.js": {noop_tracerJs, map[string]*bintree{}},
+ "opcount_tracer.js": {opcount_tracerJs, map[string]*bintree{}},
+ "prestate_tracer.js": {prestate_tracerJs, map[string]*bintree{}},
+ "trigram_tracer.js": {trigram_tracerJs, map[string]*bintree{}},
+ "unigram_tracer.js": {unigram_tracerJs, map[string]*bintree{}},
}}
// RestoreAsset restores an asset under the given directory.
diff --git a/eth/tracers/internal/tracers/call_tracer.js b/eth/tracers/internal/tracers/call_tracer.js
index 3ca7377738..7da7bf216a 100644
--- a/eth/tracers/internal/tracers/call_tracer.js
+++ b/eth/tracers/internal/tracers/call_tracer.js
@@ -1,4 +1,4 @@
-// Copyright 2017 The go-ethereum Authors
+// 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
@@ -14,187 +14,14 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// callTracer is a full blown transaction tracer that extracts and reports all
-// the internal calls made by a transaction, along with any useful information.
+
+// callFrameTracer uses the new call frame tracing methods to report useful information
+// about internal messages of a transaction.
{
- // callstack is the current recursive call stack of the EVM execution.
callstack: [{}],
-
- // descended tracks whether we've just descended from an outer transaction into
- // an inner call.
- descended: false,
-
- // step is invoked for every opcode that the VM executes.
- step: function(log, db) {
- // Capture any errors immediately
- var error = log.getError();
- if (error !== undefined) {
- this.fault(log, db);
- return;
- }
- // We only care about system opcodes, faster if we pre-check once
- var syscall = (log.op.toNumber() & 0xf0) == 0xf0;
- if (syscall) {
- var op = log.op.toString();
- }
- // If a new contract is being created, add to the call stack
- if (syscall && (op == 'CREATE' || op == "CREATE2")) {
- var inOff = log.stack.peek(1).valueOf();
- var inEnd = inOff + log.stack.peek(2).valueOf();
-
- // Assemble the internal call report and store for completion
- var call = {
- type: op,
- from: toHex(log.contract.getAddress()),
- input: toHex(log.memory.slice(inOff, inEnd)),
- gasIn: log.getGas(),
- gasCost: log.getCost(),
- value: '0x' + log.stack.peek(0).toString(16)
- };
- this.callstack.push(call);
- this.descended = true
- return;
- }
- // If a contract is being self destructed, gather that as a subcall too
- if (syscall && op == 'SELFDESTRUCT') {
- var left = this.callstack.length;
- if (this.callstack[left-1].calls === undefined) {
- this.callstack[left-1].calls = [];
- }
- this.callstack[left-1].calls.push({
- type: op,
- from: toHex(log.contract.getAddress()),
- to: toHex(toAddress(log.stack.peek(0).toString(16))),
- gasIn: log.getGas(),
- gasCost: log.getCost(),
- value: '0x' + db.getBalance(log.contract.getAddress()).toString(16)
- });
- return
- }
- // If a new method invocation is being done, add to the call stack
- if (syscall && (op == 'CALL' || op == 'CALLCODE' || op == 'DELEGATECALL' || op == 'STATICCALL')) {
- // Skip any pre-compile invocations, those are just fancy opcodes
- var to = toAddress(log.stack.peek(1).toString(16));
- if (isPrecompiled(to)) {
- return
- }
- var off = (op == 'DELEGATECALL' || op == 'STATICCALL' ? 0 : 1);
-
- var inOff = log.stack.peek(2 + off).valueOf();
- var inEnd = inOff + log.stack.peek(3 + off).valueOf();
-
- // Assemble the internal call report and store for completion
- var call = {
- type: op,
- from: toHex(log.contract.getAddress()),
- to: toHex(to),
- input: toHex(log.memory.slice(inOff, inEnd)),
- gasIn: log.getGas(),
- gasCost: log.getCost(),
- outOff: log.stack.peek(4 + off).valueOf(),
- outLen: log.stack.peek(5 + off).valueOf()
- };
- if (op != 'DELEGATECALL' && op != 'STATICCALL') {
- call.value = '0x' + log.stack.peek(2).toString(16);
- }
- this.callstack.push(call);
- this.descended = true
- return;
- }
- // If we've just descended into an inner call, retrieve it's true allowance. We
- // need to extract if from within the call as there may be funky gas dynamics
- // with regard to requested and actually given gas (2300 stipend, 63/64 rule).
- if (this.descended) {
- if (log.getDepth() >= this.callstack.length) {
- this.callstack[this.callstack.length - 1].gas = log.getGas();
- } else {
- // TODO(karalabe): The call was made to a plain account. We currently don't
- // have access to the true gas amount inside the call and so any amount will
- // mostly be wrong since it depends on a lot of input args. Skip gas for now.
- }
- this.descended = false;
- }
- // If an existing call is returning, pop off the call stack
- if (syscall && op == 'REVERT') {
- this.callstack[this.callstack.length - 1].error = "execution reverted";
- return;
- }
- if (log.getDepth() == this.callstack.length - 1) {
- // Pop off the last call and get the execution results
- var call = this.callstack.pop();
-
- if (call.type == 'CREATE' || call.type == "CREATE2") {
- // If the call was a CREATE, retrieve the contract address and output code
- call.gasUsed = '0x' + bigInt(call.gasIn - call.gasCost - log.getGas()).toString(16);
- delete call.gasIn; delete call.gasCost;
-
- var ret = log.stack.peek(0);
- if (!ret.equals(0)) {
- call.to = toHex(toAddress(ret.toString(16)));
- call.output = toHex(db.getCode(toAddress(ret.toString(16))));
- } else if (call.error === undefined) {
- call.error = "internal failure"; // TODO(karalabe): surface these faults somehow
- }
- } else {
- // If the call was a contract call, retrieve the gas usage and output
- if (call.gas !== undefined) {
- call.gasUsed = '0x' + bigInt(call.gasIn - call.gasCost + call.gas - log.getGas()).toString(16);
- }
- var ret = log.stack.peek(0);
- if (!ret.equals(0)) {
- call.output = toHex(log.memory.slice(call.outOff, call.outOff + call.outLen));
- } else if (call.error === undefined) {
- call.error = "internal failure"; // TODO(karalabe): surface these faults somehow
- }
- delete call.gasIn; delete call.gasCost;
- delete call.outOff; delete call.outLen;
- }
- if (call.gas !== undefined) {
- call.gas = '0x' + bigInt(call.gas).toString(16);
- }
- // Inject the call into the previous one
- var left = this.callstack.length;
- if (this.callstack[left-1].calls === undefined) {
- this.callstack[left-1].calls = [];
- }
- this.callstack[left-1].calls.push(call);
- }
- },
-
- // fault is invoked when the actual execution of an opcode fails.
- fault: function(log, db) {
- // If the topmost call already reverted, don't handle the additional fault again
- if (this.callstack[this.callstack.length - 1].error !== undefined) {
- return;
- }
- // Pop off the just failed call
- var call = this.callstack.pop();
- call.error = log.getError();
-
- // Consume all available gas and clean any leftovers
- if (call.gas !== undefined) {
- call.gas = '0x' + bigInt(call.gas).toString(16);
- call.gasUsed = call.gas
- }
- delete call.gasIn; delete call.gasCost;
- delete call.outOff; delete call.outLen;
-
- // Flatten the failed call into its parent
- var left = this.callstack.length;
- if (left > 0) {
- if (this.callstack[left-1].calls === undefined) {
- this.callstack[left-1].calls = [];
- }
- this.callstack[left-1].calls.push(call);
- return;
- }
- // Last call failed too, leave it in the stack
- this.callstack.push(call);
- },
-
- // result is invoked when all the opcodes have been iterated over and returns
- // the final result of the tracing.
+ fault: function(log, db) {},
result: function(ctx, db) {
+ // Prepare outer message info
var result = {
type: ctx.type,
from: toHex(ctx.from),
@@ -204,22 +31,55 @@
gasUsed: '0x' + bigInt(ctx.gasUsed).toString(16),
input: toHex(ctx.input),
output: toHex(ctx.output),
- time: ctx.time,
- };
+ }
if (this.callstack[0].calls !== undefined) {
- result.calls = this.callstack[0].calls;
+ result.calls = this.callstack[0].calls
}
if (this.callstack[0].error !== undefined) {
- result.error = this.callstack[0].error;
+ result.error = this.callstack[0].error
} else if (ctx.error !== undefined) {
- result.error = ctx.error;
+ result.error = ctx.error
}
if (result.error !== undefined && (result.error !== "execution reverted" || result.output ==="0x")) {
- delete result.output;
+ delete result.output
}
- return this.finalize(result);
- },
+ return this.finalize(result)
+ },
+ enter: function(frame) {
+ var call = {
+ type: frame.getType(),
+ from: toHex(frame.getFrom()),
+ to: toHex(frame.getTo()),
+ input: toHex(frame.getInput()),
+ gas: '0x' + bigInt(frame.getGas()).toString('16'),
+ }
+ if (frame.getValue() !== undefined){
+ call.value='0x' + bigInt(frame.getValue()).toString(16)
+ }
+ this.callstack.push(call)
+ },
+ exit: function(frameResult) {
+ var len = this.callstack.length
+ if (len > 1) {
+ var call = this.callstack.pop()
+ call.gasUsed = '0x' + bigInt(frameResult.getGasUsed()).toString('16')
+ var error = frameResult.getError()
+ if (error === undefined) {
+ call.output = toHex(frameResult.getOutput())
+ } else {
+ call.error = error
+ if (call.type === 'CREATE' || call.type === 'CREATE2') {
+ delete call.to
+ }
+ }
+ len -= 1
+ if (this.callstack[len-1].calls === undefined) {
+ this.callstack[len-1].calls = []
+ }
+ this.callstack[len-1].calls.push(call)
+ }
+ },
// finalize recreates a call object using the final desired field oder for json
// serialization. This is a nicety feature to pass meaningfully ordered results
// to users who don't interpret it, just display it.
@@ -239,14 +99,14 @@
}
for (var key in sorted) {
if (sorted[key] === undefined) {
- delete sorted[key];
+ delete sorted[key]
}
}
if (sorted.calls !== undefined) {
for (var i=0; i.
+
+// callTracer is a full blown transaction tracer that extracts and reports all
+// the internal calls made by a transaction, along with any useful information.
+{
+ // callstack is the current recursive call stack of the EVM execution.
+ callstack: [{}],
+
+ // descended tracks whether we've just descended from an outer transaction into
+ // an inner call.
+ descended: false,
+
+ // step is invoked for every opcode that the VM executes.
+ step: function(log, db) {
+ // Capture any errors immediately
+ var error = log.getError();
+ if (error !== undefined) {
+ this.fault(log, db);
+ return;
+ }
+ // We only care about system opcodes, faster if we pre-check once
+ var syscall = (log.op.toNumber() & 0xf0) == 0xf0;
+ if (syscall) {
+ var op = log.op.toString();
+ }
+ // If a new contract is being created, add to the call stack
+ if (syscall && (op == 'CREATE' || op == "CREATE2")) {
+ var inOff = log.stack.peek(1).valueOf();
+ var inEnd = inOff + log.stack.peek(2).valueOf();
+
+ // Assemble the internal call report and store for completion
+ var call = {
+ type: op,
+ from: toHex(log.contract.getAddress()),
+ input: toHex(log.memory.slice(inOff, inEnd)),
+ gasIn: log.getGas(),
+ gasCost: log.getCost(),
+ value: '0x' + log.stack.peek(0).toString(16)
+ };
+ this.callstack.push(call);
+ this.descended = true
+ return;
+ }
+ // If a contract is being self destructed, gather that as a subcall too
+ if (syscall && op == 'SELFDESTRUCT') {
+ var left = this.callstack.length;
+ if (this.callstack[left-1].calls === undefined) {
+ this.callstack[left-1].calls = [];
+ }
+ this.callstack[left-1].calls.push({
+ type: op,
+ from: toHex(log.contract.getAddress()),
+ to: toHex(toAddress(log.stack.peek(0).toString(16))),
+ gasIn: log.getGas(),
+ gasCost: log.getCost(),
+ value: '0x' + db.getBalance(log.contract.getAddress()).toString(16)
+ });
+ return
+ }
+ // If a new method invocation is being done, add to the call stack
+ if (syscall && (op == 'CALL' || op == 'CALLCODE' || op == 'DELEGATECALL' || op == 'STATICCALL')) {
+ // Skip any pre-compile invocations, those are just fancy opcodes
+ var to = toAddress(log.stack.peek(1).toString(16));
+ if (isPrecompiled(to)) {
+ return
+ }
+ var off = (op == 'DELEGATECALL' || op == 'STATICCALL' ? 0 : 1);
+
+ var inOff = log.stack.peek(2 + off).valueOf();
+ var inEnd = inOff + log.stack.peek(3 + off).valueOf();
+
+ // Assemble the internal call report and store for completion
+ var call = {
+ type: op,
+ from: toHex(log.contract.getAddress()),
+ to: toHex(to),
+ input: toHex(log.memory.slice(inOff, inEnd)),
+ gasIn: log.getGas(),
+ gasCost: log.getCost(),
+ outOff: log.stack.peek(4 + off).valueOf(),
+ outLen: log.stack.peek(5 + off).valueOf()
+ };
+ if (op != 'DELEGATECALL' && op != 'STATICCALL') {
+ call.value = '0x' + log.stack.peek(2).toString(16);
+ }
+ this.callstack.push(call);
+ this.descended = true
+ return;
+ }
+ // If we've just descended into an inner call, retrieve it's true allowance. We
+ // need to extract if from within the call as there may be funky gas dynamics
+ // with regard to requested and actually given gas (2300 stipend, 63/64 rule).
+ if (this.descended) {
+ if (log.getDepth() >= this.callstack.length) {
+ this.callstack[this.callstack.length - 1].gas = log.getGas();
+ } else {
+ // TODO(karalabe): The call was made to a plain account. We currently don't
+ // have access to the true gas amount inside the call and so any amount will
+ // mostly be wrong since it depends on a lot of input args. Skip gas for now.
+ }
+ this.descended = false;
+ }
+ // If an existing call is returning, pop off the call stack
+ if (syscall && op == 'REVERT') {
+ this.callstack[this.callstack.length - 1].error = "execution reverted";
+ return;
+ }
+ if (log.getDepth() == this.callstack.length - 1) {
+ // Pop off the last call and get the execution results
+ var call = this.callstack.pop();
+
+ if (call.type == 'CREATE' || call.type == "CREATE2") {
+ // If the call was a CREATE, retrieve the contract address and output code
+ call.gasUsed = '0x' + bigInt(call.gasIn - call.gasCost - log.getGas()).toString(16);
+ delete call.gasIn; delete call.gasCost;
+
+ var ret = log.stack.peek(0);
+ if (!ret.equals(0)) {
+ call.to = toHex(toAddress(ret.toString(16)));
+ call.output = toHex(db.getCode(toAddress(ret.toString(16))));
+ } else if (call.error === undefined) {
+ call.error = "internal failure"; // TODO(karalabe): surface these faults somehow
+ }
+ } else {
+ // If the call was a contract call, retrieve the gas usage and output
+ if (call.gas !== undefined) {
+ call.gasUsed = '0x' + bigInt(call.gasIn - call.gasCost + call.gas - log.getGas()).toString(16);
+ }
+ var ret = log.stack.peek(0);
+ if (!ret.equals(0)) {
+ call.output = toHex(log.memory.slice(call.outOff, call.outOff + call.outLen));
+ } else if (call.error === undefined) {
+ call.error = "internal failure"; // TODO(karalabe): surface these faults somehow
+ }
+ delete call.gasIn; delete call.gasCost;
+ delete call.outOff; delete call.outLen;
+ }
+ if (call.gas !== undefined) {
+ call.gas = '0x' + bigInt(call.gas).toString(16);
+ }
+ // Inject the call into the previous one
+ var left = this.callstack.length;
+ if (this.callstack[left-1].calls === undefined) {
+ this.callstack[left-1].calls = [];
+ }
+ this.callstack[left-1].calls.push(call);
+ }
+ },
+
+ // fault is invoked when the actual execution of an opcode fails.
+ fault: function(log, db) {
+ // If the topmost call already reverted, don't handle the additional fault again
+ if (this.callstack[this.callstack.length - 1].error !== undefined) {
+ return;
+ }
+ // Pop off the just failed call
+ var call = this.callstack.pop();
+ call.error = log.getError();
+
+ // Consume all available gas and clean any leftovers
+ if (call.gas !== undefined) {
+ call.gas = '0x' + bigInt(call.gas).toString(16);
+ call.gasUsed = call.gas
+ }
+ delete call.gasIn; delete call.gasCost;
+ delete call.outOff; delete call.outLen;
+
+ // Flatten the failed call into its parent
+ var left = this.callstack.length;
+ if (left > 0) {
+ if (this.callstack[left-1].calls === undefined) {
+ this.callstack[left-1].calls = [];
+ }
+ this.callstack[left-1].calls.push(call);
+ return;
+ }
+ // Last call failed too, leave it in the stack
+ this.callstack.push(call);
+ },
+
+ // result is invoked when all the opcodes have been iterated over and returns
+ // the final result of the tracing.
+ result: function(ctx, db) {
+ var result = {
+ type: ctx.type,
+ from: toHex(ctx.from),
+ to: toHex(ctx.to),
+ value: '0x' + ctx.value.toString(16),
+ gas: '0x' + bigInt(ctx.gas).toString(16),
+ gasUsed: '0x' + bigInt(ctx.gasUsed).toString(16),
+ input: toHex(ctx.input),
+ output: toHex(ctx.output),
+ time: ctx.time,
+ };
+ if (this.callstack[0].calls !== undefined) {
+ result.calls = this.callstack[0].calls;
+ }
+ if (this.callstack[0].error !== undefined) {
+ result.error = this.callstack[0].error;
+ } else if (ctx.error !== undefined) {
+ result.error = ctx.error;
+ }
+ if (result.error !== undefined && (result.error !== "execution reverted" || result.output ==="0x")) {
+ delete result.output;
+ }
+ return this.finalize(result);
+ },
+
+ // finalize recreates a call object using the final desired field oder for json
+ // serialization. This is a nicety feature to pass meaningfully ordered results
+ // to users who don't interpret it, just display it.
+ finalize: function(call) {
+ var sorted = {
+ type: call.type,
+ from: call.from,
+ to: call.to,
+ value: call.value,
+ gas: call.gas,
+ gasUsed: call.gasUsed,
+ input: call.input,
+ output: call.output,
+ error: call.error,
+ time: call.time,
+ calls: call.calls,
+ }
+ for (var key in sorted) {
+ if (sorted[key] === undefined) {
+ delete sorted[key];
+ }
+ }
+ if (sorted.calls !== undefined) {
+ for (var i=0; i 0 {
jst.err = jst.reason
+ env.Cancel()
return
}
jst.opWrapper.op = op
@@ -649,41 +760,67 @@ func (jst *Tracer) CaptureEnd(output []byte, gasUsed uint64, t time.Duration, er
}
}
+// CaptureEnter is called when EVM enters a new scope (via call, create or selfdestruct).
+func (jst *Tracer) CaptureEnter(typ vm.OpCode, from common.Address, to common.Address, input []byte, gas uint64, value *big.Int) {
+ if !jst.traceCallFrames {
+ return
+ }
+ if jst.err != nil {
+ return
+ }
+ // If tracing was interrupted, set the error and stop
+ if atomic.LoadUint32(&jst.interrupt) > 0 {
+ jst.err = jst.reason
+ return
+ }
+
+ *jst.frame.typ = typ.String()
+ *jst.frame.from = from
+ *jst.frame.to = to
+ jst.frame.input = common.CopyBytes(input)
+ *jst.frame.gas = uint(gas)
+ jst.frame.value = nil
+ if value != nil {
+ jst.frame.value = new(big.Int).SetBytes(value.Bytes())
+ }
+
+ if _, err := jst.call(true, "enter", "frame"); err != nil {
+ jst.err = wrapError("enter", err)
+ }
+}
+
+// CaptureExit is called when EVM exits a scope, even if the scope didn't
+// execute any code.
+func (jst *Tracer) CaptureExit(output []byte, gasUsed uint64, err error) {
+ if !jst.traceCallFrames {
+ return
+ }
+ // If tracing was interrupted, set the error and stop
+ if atomic.LoadUint32(&jst.interrupt) > 0 {
+ jst.err = jst.reason
+ return
+ }
+
+ jst.frameResult.output = common.CopyBytes(output)
+ *jst.frameResult.gasUsed = uint(gasUsed)
+ jst.frameResult.errorValue = nil
+ if err != nil {
+ jst.frameResult.errorValue = new(string)
+ *jst.frameResult.errorValue = err.Error()
+ }
+
+ if _, err := jst.call(true, "exit", "frameResult"); err != nil {
+ jst.err = wrapError("exit", err)
+ }
+}
+
// GetResult calls the Javascript 'result' function and returns its value, or any accumulated error
func (jst *Tracer) GetResult() (json.RawMessage, error) {
// Transform the context into a JavaScript object and inject into the state
obj := jst.vm.PushObject()
for key, val := range jst.ctx {
- switch val := val.(type) {
- case uint64:
- jst.vm.PushUint(uint(val))
-
- case string:
- jst.vm.PushString(val)
-
- case []byte:
- ptr := jst.vm.PushFixedBuffer(len(val))
- copy(makeSlice(ptr, uint(len(val))), val)
-
- case common.Address:
- ptr := jst.vm.PushFixedBuffer(20)
- copy(makeSlice(ptr, 20), val[:])
-
- case *big.Int:
- pushBigInt(val, jst.vm)
-
- case int:
- jst.vm.PushInt(val)
-
- case common.Hash:
- ptr := jst.vm.PushFixedBuffer(32)
- copy(makeSlice(ptr, 32), val[:])
-
- default:
- panic(fmt.Sprintf("unsupported type: %T", val))
- }
- jst.vm.PutPropString(obj, key)
+ jst.addToObj(obj, key, val)
}
jst.vm.PutPropString(jst.stateObject, "ctx")
@@ -698,3 +835,35 @@ func (jst *Tracer) GetResult() (json.RawMessage, error) {
return result, jst.err
}
+
+// addToObj pushes a field to a JS object.
+func (jst *Tracer) addToObj(obj int, key string, val interface{}) {
+ pushValue(jst.vm, val)
+ jst.vm.PutPropString(obj, key)
+}
+
+func pushValue(ctx *duktape.Context, val interface{}) {
+ switch val := val.(type) {
+ case uint64:
+ ctx.PushUint(uint(val))
+ case string:
+ ctx.PushString(val)
+ case []byte:
+ ptr := ctx.PushFixedBuffer(len(val))
+ copy(makeSlice(ptr, uint(len(val))), val)
+ case common.Address:
+ ptr := ctx.PushFixedBuffer(20)
+ copy(makeSlice(ptr, 20), val[:])
+ case *big.Int:
+ pushBigInt(val, ctx)
+ case int:
+ ctx.PushInt(val)
+ case uint:
+ ctx.PushUint(val)
+ case common.Hash:
+ ptr := ctx.PushFixedBuffer(32)
+ copy(makeSlice(ptr, 32), val[:])
+ default:
+ panic(fmt.Sprintf("unsupported type: %T", val))
+ }
+}
diff --git a/eth/tracers/tracer_test.go b/eth/tracers/tracer_test.go
index d2d3e57c4a..63b09bdc6f 100644
--- a/eth/tracers/tracer_test.go
+++ b/eth/tracers/tracer_test.go
@@ -207,7 +207,7 @@ func TestNoStepExec(t *testing.T) {
}
func TestIsPrecompile(t *testing.T) {
- chaincfg := ¶ms.ChainConfig{ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: false, EIP150Block: big.NewInt(0), EIP150Hash: common.Hash{}, EIP155Block: big.NewInt(0), EIP158Block: big.NewInt(0), ByzantiumBlock: big.NewInt(100), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(200), MuirGlacierBlock: big.NewInt(0), BerlinBlock: big.NewInt(300), LondonBlock: big.NewInt(0), CatalystBlock: nil, Ethash: new(params.EthashConfig), Clique: nil}
+ chaincfg := ¶ms.ChainConfig{ChainID: big.NewInt(1), HomesteadBlock: big.NewInt(0), DAOForkBlock: nil, DAOForkSupport: false, EIP150Block: big.NewInt(0), EIP150Hash: common.Hash{}, EIP155Block: big.NewInt(0), EIP158Block: big.NewInt(0), ByzantiumBlock: big.NewInt(100), ConstantinopleBlock: big.NewInt(0), PetersburgBlock: big.NewInt(0), IstanbulBlock: big.NewInt(200), MuirGlacierBlock: big.NewInt(0), BerlinBlock: big.NewInt(300), LondonBlock: big.NewInt(0), TerminalTotalDifficulty: nil, Ethash: new(params.EthashConfig), Clique: nil}
chaincfg.ByzantiumBlock = big.NewInt(100)
chaincfg.IstanbulBlock = big.NewInt(200)
chaincfg.BerlinBlock = big.NewInt(300)
@@ -236,3 +236,35 @@ func TestIsPrecompile(t *testing.T) {
t.Errorf("Tracer should consider blake2f as precompile in istanbul")
}
}
+
+func TestEnterExit(t *testing.T) {
+ // test that either both or none of enter() and exit() are defined
+ if _, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}}", new(Context)); err == nil {
+ t.Fatal("tracer creation should've failed without exit() definition")
+ }
+ if _, err := New("{step: function() {}, fault: function() {}, result: function() { return null; }, enter: function() {}, exit: function() {}}", new(Context)); err != nil {
+ t.Fatal(err)
+ }
+
+ // test that the enter and exit method are correctly invoked and the values passed
+ tracer, err := New("{enters: 0, exits: 0, enterGas: 0, gasUsed: 0, step: function() {}, fault: function() {}, result: function() { return {enters: this.enters, exits: this.exits, enterGas: this.enterGas, gasUsed: this.gasUsed} }, enter: function(frame) { this.enters++; this.enterGas = frame.getGas(); }, exit: function(res) { this.exits++; this.gasUsed = res.getGasUsed(); }}", new(Context))
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ scope := &vm.ScopeContext{
+ Contract: vm.NewContract(&account{}, &account{}, big.NewInt(0), 0),
+ }
+
+ tracer.CaptureEnter(vm.CALL, scope.Contract.Caller(), scope.Contract.Address(), []byte{}, 1000, new(big.Int))
+ tracer.CaptureExit([]byte{}, 400, nil)
+
+ have, err := tracer.GetResult()
+ if err != nil {
+ t.Fatal(err)
+ }
+ want := `{"enters":1,"exits":1,"enterGas":1000,"gasUsed":400}`
+ if string(have) != want {
+ t.Errorf("Number of invocations of enter() and exit() is wrong. Have %s, want %s\n", have, want)
+ }
+}
diff --git a/eth/tracers/tracers_test.go b/eth/tracers/tracers_test.go
index 8fbbf154b4..f2333b87b5 100644
--- a/eth/tracers/tracers_test.go
+++ b/eth/tracers/tracers_test.go
@@ -120,6 +120,85 @@ type callTracerTest struct {
Result *callTrace `json:"result"`
}
+// TestZeroValueToNotExitCall tests the calltracer(s) on the following:
+// Tx to A, A calls B with zero value. B does not already exist.
+// Expected: that enter/exit is invoked and the inner call is shown in the result
+func TestZeroValueToNotExitCall(t *testing.T) {
+ var to = common.HexToAddress("0x00000000000000000000000000000000deadbeef")
+ privkey, err := crypto.HexToECDSA("0000000000000000deadbeef00000000000000000000000000000000deadbeef")
+ if err != nil {
+ t.Fatalf("err %v", err)
+ }
+ signer := types.NewEIP155Signer(big.NewInt(1))
+ tx, err := types.SignNewTx(privkey, signer, &types.LegacyTx{
+ GasPrice: big.NewInt(0),
+ Gas: 50000,
+ To: &to,
+ })
+ if err != nil {
+ t.Fatalf("err %v", err)
+ }
+ origin, _ := signer.Sender(tx)
+ txContext := vm.TxContext{
+ Origin: origin,
+ GasPrice: big.NewInt(1),
+ }
+ context := vm.BlockContext{
+ CanTransfer: core.CanTransfer,
+ Transfer: core.Transfer,
+ Coinbase: common.Address{},
+ BlockNumber: new(big.Int).SetUint64(8000000),
+ Time: new(big.Int).SetUint64(5),
+ Difficulty: big.NewInt(0x30000),
+ GasLimit: uint64(6000000),
+ }
+ var code = []byte{
+ byte(vm.PUSH1), 0x0, byte(vm.DUP1), byte(vm.DUP1), byte(vm.DUP1), // in and outs zero
+ byte(vm.DUP1), byte(vm.PUSH1), 0xff, byte(vm.GAS), // value=0,address=0xff, gas=GAS
+ byte(vm.CALL),
+ }
+ var alloc = core.GenesisAlloc{
+ to: core.GenesisAccount{
+ Nonce: 1,
+ Code: code,
+ },
+ origin: core.GenesisAccount{
+ Nonce: 0,
+ Balance: big.NewInt(500000000000000),
+ },
+ }
+ _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), alloc, false)
+ // Create the tracer, the EVM environment and run it
+ tracer, err := New("callTracer", new(Context))
+ if err != nil {
+ t.Fatalf("failed to create call tracer: %v", err)
+ }
+ evm := vm.NewEVM(context, txContext, statedb, params.MainnetChainConfig, vm.Config{Debug: true, Tracer: tracer})
+ msg, err := tx.AsMessage(signer, 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(); err != nil {
+ t.Fatalf("failed to execute transaction: %v", err)
+ }
+ // Retrieve the trace result and compare against the etalon
+ res, err := tracer.GetResult()
+ if err != nil {
+ t.Fatalf("failed to retrieve trace result: %v", err)
+ }
+ have := new(callTrace)
+ if err := json.Unmarshal(res, have); err != nil {
+ t.Fatalf("failed to unmarshal trace result: %v", err)
+ }
+ wantStr := `{"type":"CALL","from":"0x682a80a6f560eec50d54e63cbeda1c324c5f8d1b","to":"0x00000000000000000000000000000000deadbeef","value":"0x0","gas":"0x7148","gasUsed":"0x2d0","input":"0x","output":"0x","calls":[{"type":"CALL","from":"0x00000000000000000000000000000000deadbeef","to":"0x00000000000000000000000000000000000000ff","value":"0x0","gas":"0x6cbf","gasUsed":"0x0","input":"0x","output":"0x"}]}`
+ want := new(callTrace)
+ json.Unmarshal([]byte(wantStr), want)
+ if !jsonEqual(have, want) {
+ t.Error("have != want")
+ }
+}
+
func TestPrestateTracerCreate2(t *testing.T) {
unsignedTx := types.NewTransaction(1, common.HexToAddress("0x00000000000000000000000000000000deadbeef"),
new(big.Int), 5000000, big.NewInt(1), []byte{})
@@ -203,21 +282,25 @@ func TestPrestateTracerCreate2(t *testing.T) {
// Iterates over all the input-output datasets in the tracer test harness and
// runs the JavaScript tracers against them.
-func TestCallTracer(t *testing.T) {
- files, err := ioutil.ReadDir("testdata")
+func TestCallTracerLegacy(t *testing.T) {
+ testCallTracer("callTracerLegacy", "call_tracer_legacy", t)
+}
+
+func testCallTracer(tracer string, dirPath string, t *testing.T) {
+ files, err := ioutil.ReadDir(filepath.Join("testdata", dirPath))
if err != nil {
t.Fatalf("failed to retrieve tracer test suite: %v", err)
}
for _, file := range files {
- if !strings.HasPrefix(file.Name(), "call_tracer_") {
+ if !strings.HasSuffix(file.Name(), ".json") {
continue
}
file := file // capture range variable
- t.Run(camel(strings.TrimSuffix(strings.TrimPrefix(file.Name(), "call_tracer_"), ".json")), func(t *testing.T) {
+ t.Run(camel(strings.TrimSuffix(file.Name(), ".json")), func(t *testing.T) {
t.Parallel()
// Call tracer test found, read if from disk
- blob, err := ioutil.ReadFile(filepath.Join("testdata", file.Name()))
+ blob, err := ioutil.ReadFile(filepath.Join("testdata", dirPath, file.Name()))
if err != nil {
t.Fatalf("failed to read testcase: %v", err)
}
@@ -248,7 +331,7 @@ func TestCallTracer(t *testing.T) {
_, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)
// Create the tracer, the EVM environment and run it
- tracer, err := New("callTracer", new(Context))
+ tracer, err := New(tracer, new(Context))
if err != nil {
t.Fatalf("failed to create call tracer: %v", err)
}
@@ -283,6 +366,10 @@ func TestCallTracer(t *testing.T) {
}
}
+func TestCallTracer(t *testing.T) {
+ testCallTracer("callTracer", "call_tracer", t)
+}
+
// jsonEqual is similar to reflect.DeepEqual, but does a 'bounce' via json prior to
// comparison
func jsonEqual(x, y interface{}) bool {
@@ -329,6 +416,7 @@ func BenchmarkTransactionTrace(b *testing.B) {
Time: new(big.Int).SetUint64(uint64(5)),
Difficulty: big.NewInt(0xffffffff),
GasLimit: gas,
+ BaseFee: big.NewInt(8),
}
alloc := core.GenesisAlloc{}
// The code pushes 'deadbeef' into memory, then the other params, and calls CREATE2, then returns
@@ -353,8 +441,8 @@ func BenchmarkTransactionTrace(b *testing.B) {
tracer := vm.NewStructLogger(&vm.LogConfig{
Debug: false,
//DisableStorage: true,
- //DisableMemory: true,
- //DisableReturnData: true,
+ //EnableMemory: false,
+ //EnableReturnData: false,
})
evm := vm.NewEVM(context, txContext, statedb, params.AllEthashProtocolChanges, vm.Config{Debug: true, Tracer: tracer})
msg, err := tx.AsMessage(signer, nil)
@@ -378,3 +466,73 @@ func BenchmarkTransactionTrace(b *testing.B) {
tracer.Reset()
}
}
+
+func BenchmarkTracers(b *testing.B) {
+ files, err := ioutil.ReadDir(filepath.Join("testdata", "call_tracer"))
+ if err != nil {
+ b.Fatalf("failed to retrieve tracer test suite: %v", err)
+ }
+ for _, file := range files {
+ if !strings.HasSuffix(file.Name(), ".json") {
+ continue
+ }
+ 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()))
+ if err != nil {
+ b.Fatalf("failed to read testcase: %v", err)
+ }
+ test := new(callTracerTest)
+ if err := json.Unmarshal(blob, test); err != nil {
+ b.Fatalf("failed to parse testcase: %v", err)
+ }
+ benchTracer("callTracer", test, b)
+ })
+ }
+}
+
+func benchTracer(tracerName string, test *callTracerTest, b *testing.B) {
+ // Configure a blockchain with the given prestate
+ tx := new(types.Transaction)
+ if err := rlp.DecodeBytes(common.FromHex(test.Input), tx); err != nil {
+ 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)
+ if err != nil {
+ b.Fatalf("failed to prepare transaction for tracing: %v", err)
+ }
+ origin, _ := signer.Sender(tx)
+ txContext := vm.TxContext{
+ Origin: origin,
+ GasPrice: tx.GasPrice(),
+ }
+ context := vm.BlockContext{
+ CanTransfer: core.CanTransfer,
+ Transfer: core.Transfer,
+ Coinbase: test.Context.Miner,
+ BlockNumber: new(big.Int).SetUint64(uint64(test.Context.Number)),
+ Time: new(big.Int).SetUint64(uint64(test.Context.Time)),
+ Difficulty: (*big.Int)(test.Context.Difficulty),
+ GasLimit: uint64(test.Context.GasLimit),
+ }
+ _, statedb := tests.MakePreState(rawdb.NewMemoryDatabase(), test.Genesis.Alloc, false)
+
+ // Create the tracer, the EVM environment and run it
+ tracer, err := New(tracerName, new(Context))
+ if err != nil {
+ b.Fatalf("failed to create call tracer: %v", err)
+ }
+ evm := vm.NewEVM(context, txContext, statedb, test.Genesis.Config, vm.Config{Debug: true, Tracer: tracer})
+
+ b.ReportAllocs()
+ b.ResetTimer()
+ for i := 0; i < b.N; i++ {
+ snap := statedb.Snapshot()
+ st := core.NewStateTransition(evm, msg, new(core.GasPool).AddGas(tx.Gas()))
+ if _, err = st.TransitionDb(); err != nil {
+ b.Fatalf("failed to execute transaction: %v", err)
+ }
+ statedb.RevertToSnapshot(snap)
+ }
+}
diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go
index 9f68323134..18e0a49418 100644
--- a/ethclient/ethclient.go
+++ b/ethclient/ethclient.go
@@ -60,7 +60,7 @@ func (ec *Client) Close() {
// Blockchain Access
-// ChainId retrieves the current chain ID for transaction replay protection.
+// ChainID retrieves the current chain ID for transaction replay protection.
func (ec *Client) ChainID(ctx context.Context) (*big.Int, error) {
var result hexutil.Big
err := ec.c.CallContext(ctx, &result, "eth_chainId")
diff --git a/ethclient/gethclient/gethclient_test.go b/ethclient/gethclient/gethclient_test.go
index c0b9d0c2d0..ca2cb1abda 100644
--- a/ethclient/gethclient/gethclient_test.go
+++ b/ethclient/gethclient/gethclient_test.go
@@ -97,37 +97,40 @@ func TestGethClient(t *testing.T) {
defer backend.Close()
defer client.Close()
- tests := map[string]struct {
+ tests := []struct {
+ name string
test func(t *testing.T)
}{
- "TestAccessList": {
+ {
+ "TestAccessList",
func(t *testing.T) { testAccessList(t, client) },
},
- "TestGetProof": {
+ {
+ "TestGetProof",
func(t *testing.T) { testGetProof(t, client) },
- },
- "TestGCStats": {
+ }, {
+ "TestGCStats",
func(t *testing.T) { testGCStats(t, client) },
- },
- "TestMemStats": {
+ }, {
+ "TestMemStats",
func(t *testing.T) { testMemStats(t, client) },
- },
- "TestGetNodeInfo": {
+ }, {
+ "TestGetNodeInfo",
func(t *testing.T) { testGetNodeInfo(t, client) },
- },
- "TestSetHead": {
+ }, {
+ "TestSetHead",
func(t *testing.T) { testSetHead(t, client) },
- },
- "TestSubscribePendingTxs": {
+ }, {
+ "TestSubscribePendingTxs",
func(t *testing.T) { testSubscribePendingTransactions(t, client) },
- },
- "TestCallContract": {
+ }, {
+ "TestCallContract",
func(t *testing.T) { testCallContract(t, client) },
},
}
t.Parallel()
- for name, tt := range tests {
- t.Run(name, tt.test)
+ for _, tt := range tests {
+ t.Run(tt.name, tt.test)
}
}
diff --git a/ethdb/database.go b/ethdb/database.go
index bdc09d5e98..0a5729c6c1 100644
--- a/ethdb/database.go
+++ b/ethdb/database.go
@@ -76,12 +76,12 @@ type AncientReader interface {
// Ancient retrieves an ancient binary blob from the append-only immutable files.
Ancient(kind string, number uint64) ([]byte, error)
- // ReadAncients retrieves multiple items in sequence, starting from the index 'start'.
+ // AncientRange retrieves multiple items in sequence, starting from the index 'start'.
// It will return
// - at most 'count' items,
// - at least 1 item (even if exceeding the maxBytes), but will otherwise
// return as many items as fit into maxBytes.
- ReadAncients(kind string, start, count, maxBytes uint64) ([][]byte, error)
+ AncientRange(kind string, start, count, maxBytes uint64) ([][]byte, error)
// Ancients returns the ancient item numbers in the ancient store.
Ancients() (uint64, error)
@@ -90,11 +90,21 @@ type AncientReader interface {
AncientSize(kind string) (uint64, error)
}
+// AncientBatchReader is the interface for 'batched' or 'atomic' reading.
+type AncientBatchReader interface {
+ AncientReader
+
+ // ReadAncients runs the given read operation while ensuring that no writes take place
+ // on the underlying freezer.
+ ReadAncients(fn func(AncientReader) error) (err error)
+}
+
// AncientWriter contains the methods required to write to immutable ancient data.
type AncientWriter interface {
- // AppendAncient injects all binary blobs belong to block at the end of the
- // append-only immutable table files.
- AppendAncient(number uint64, hash, header, body, receipt, td []byte) error
+ // ModifyAncients runs a write operation on the ancient store.
+ // If the function returns an error, any changes to the underlying store are reverted.
+ // The integer return value is the total size of the written data.
+ ModifyAncients(func(AncientWriteOp) error) (int64, error)
// TruncateAncients discards all but the first n ancient data from the ancient store.
TruncateAncients(n uint64) error
@@ -103,11 +113,20 @@ type AncientWriter interface {
Sync() error
}
+// AncientWriteOp is given to the function argument of ModifyAncients.
+type AncientWriteOp interface {
+ // Append adds an RLP-encoded item.
+ Append(kind string, number uint64, item interface{}) error
+
+ // AppendRaw adds an item without RLP-encoding it.
+ AppendRaw(kind string, number uint64, item []byte) error
+}
+
// Reader contains the methods required to read data from both key-value as well as
// immutable ancient data.
type Reader interface {
KeyValueReader
- AncientReader
+ AncientBatchReader
}
// Writer contains the methods required to write data to both key-value as well as
@@ -120,7 +139,7 @@ type Writer interface {
// AncientStore contains all the methods required to allow handling different
// ancient data stores backing immutable chain data store.
type AncientStore interface {
- AncientReader
+ AncientBatchReader
AncientWriter
io.Closer
}
diff --git a/ethdb/leveldb/leveldb.go b/ethdb/leveldb/leveldb.go
index 5d19cc3577..9a782dedbe 100644
--- a/ethdb/leveldb/leveldb.go
+++ b/ethdb/leveldb/leveldb.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 !js
// +build !js
// Package leveldb implements the key-value database layer based on LevelDB.
@@ -454,7 +455,7 @@ type batch struct {
// Put inserts the given value into the batch for later committing.
func (b *batch) Put(key, value []byte) error {
b.b.Put(key, value)
- b.size += len(value)
+ b.size += len(key) + len(value)
return nil
}
diff --git a/ethdb/memorydb/memorydb.go b/ethdb/memorydb/memorydb.go
index fedc9e326c..78181e860c 100644
--- a/ethdb/memorydb/memorydb.go
+++ b/ethdb/memorydb/memorydb.go
@@ -204,7 +204,7 @@ type batch struct {
// Put inserts the given value into the batch for later committing.
func (b *batch) Put(key, value []byte) error {
b.writes = append(b.writes, keyvalue{common.CopyBytes(key), common.CopyBytes(value), false})
- b.size += len(value)
+ b.size += len(key) + len(value)
return nil
}
diff --git a/ethstats/ethstats.go b/ethstats/ethstats.go
index 148359110c..55c0c880f3 100644
--- a/ethstats/ethstats.go
+++ b/ethstats/ethstats.go
@@ -30,12 +30,12 @@ import (
"sync"
"time"
+ "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/eth/downloader"
ethproto "github.com/ethereum/go-ethereum/eth/protocols/eth"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/les"
@@ -67,7 +67,7 @@ type backend interface {
HeaderByNumber(ctx context.Context, number rpc.BlockNumber) (*types.Header, error)
GetTd(ctx context.Context, hash common.Hash) *big.Int
Stats() (pending int, queued int)
- Downloader() *downloader.Downloader
+ SyncProgress() ethereum.SyncProgress
}
// fullNodeBackend encompasses the functionality necessary for a full node
@@ -777,7 +777,7 @@ func (s *Service) reportStats(conn *connWrapper) error {
mining = fullBackend.Miner().Mining()
hashrate = int(fullBackend.Miner().Hashrate())
- sync := fullBackend.Downloader().Progress()
+ sync := fullBackend.SyncProgress()
syncing = fullBackend.CurrentHeader().Number.Uint64() >= sync.HighestBlock
price, _ := fullBackend.SuggestGasTipCap(context.Background())
@@ -786,7 +786,7 @@ func (s *Service) reportStats(conn *connWrapper) error {
gasprice += int(basefee.Uint64())
}
} else {
- sync := s.backend.Downloader().Progress()
+ sync := s.backend.SyncProgress()
syncing = s.backend.CurrentHeader().Number.Uint64() >= sync.HighestBlock
}
// Assemble the node stats and send it to the server
diff --git a/go.mod b/go.mod
index ee9ffa0454..ef6b365b2d 100644
--- a/go.mod
+++ b/go.mod
@@ -19,22 +19,21 @@ require (
github.com/davecgh/go-spew v1.1.1
github.com/deckarep/golang-set v0.0.0-20180603214616-504e848d77ea
github.com/deepmap/oapi-codegen v1.8.2 // indirect
- github.com/dlclark/regexp2 v1.2.0 // indirect
github.com/docker/docker v1.4.2-0.20180625184442-8e610b2b55bf
- github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498
+ github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48
github.com/edsrzf/mmap-go v1.0.0
github.com/fatih/color v1.7.0
github.com/fjl/memsize v0.0.0-20190710130421-bcb5799ab5e5
github.com/gballet/go-libpcsclite v0.0.0-20190607065134-2772fd86a8ff
github.com/go-ole/go-ole v1.2.1 // indirect
- github.com/go-sourcemap/sourcemap v2.1.2+incompatible // indirect
github.com/go-stack/stack v1.8.0
github.com/golang/protobuf v1.4.3
- github.com/golang/snappy v0.0.3
+ github.com/golang/snappy v0.0.4
github.com/google/gofuzz v1.1.1-0.20200604201612-c04b05f3adfa
github.com/google/uuid v1.1.5
github.com/gorilla/websocket v1.4.2
github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29
+ github.com/hashicorp/go-bexpr v0.1.10
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d
github.com/holiman/bloomfilter/v2 v2.0.3
github.com/holiman/uint256 v1.2.0
@@ -45,7 +44,7 @@ require (
github.com/jackpal/go-nat-pmp v1.0.2-0.20160603034137-1fa385a6f458
github.com/jedisct1/go-minisign v0.0.0-20190909160543-45766022959e
github.com/julienschmidt/httprouter v1.2.0
- github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356
+ github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mattn/go-colorable v0.1.8
github.com/mattn/go-isatty v0.0.12
@@ -59,7 +58,7 @@ require (
github.com/shirou/gopsutil v3.21.4-0.20210419000835-c7a38de76ee5+incompatible
github.com/status-im/keycard-go v0.0.0-20190316090335-8537d3370df4
github.com/stretchr/testify v1.7.0
- github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954
+ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7
github.com/tklauser/go-sysconf v0.3.5 // indirect
github.com/tyler-smith/go-bip39 v1.0.1-0.20181017060643-dbb3b84ba2ef
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
@@ -71,6 +70,5 @@ require (
gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce
gopkg.in/olebedev/go-duktape.v3 v3.0.0-20200619000410-60c24ae608a6
gopkg.in/urfave/cli.v1 v1.20.0
- gopkg.in/yaml.v2 v2.4.0 // indirect
gotest.tools v2.2.0+incompatible // indirect
)
diff --git a/go.sum b/go.sum
index d6e9639d2e..36eb9e5084 100644
--- a/go.sum
+++ b/go.sum
@@ -104,6 +104,7 @@ github.com/consensys/bavard v0.1.8-0.20210406032232-f3452dc9b572/go.mod h1:Bpd0/
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f h1:C43yEtQ6NIf4ftFXD/V55gnGFgPbMQobd//YlnLjUJ8=
github.com/consensys/gnark-crypto v0.4.1-0.20210426202927-39ac3d4b3f1f/go.mod h1:815PAHg3wvysy0SyIqanF8gZ0Y1wjk/hrDHD/iT88+Q=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
+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/dave/jennifer v1.2.0/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
@@ -119,12 +120,13 @@ github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumC
github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
github.com/dgryski/go-bitstream v0.0.0-20180413035011-3522498ce2c8/go.mod h1:VMaSuZ+SZcx/wljOQKvp5srsbCiKDEb6K2wC4+PiBmQ=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/dlclark/regexp2 v1.2.0 h1:8sAhBGEM0dRWogWqWyQeIJnxjWO6oIjl8FKqREDsGfk=
-github.com/dlclark/regexp2 v1.2.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
+github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91 h1:Izz0+t1Z5nI16/II7vuEo/nHjodOg0p7+OiDpjX5t1E=
+github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
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-20200721192441-a695b0cdd498 h1:Y9vTBSsV4hSwPSj4bacAU/eSnV3dAxVpepaghAdhGoQ=
-github.com/dop251/goja v0.0.0-20200721192441-a695b0cdd498/go.mod h1:Mw6PkjjMXWbTj+nnj4s3QPXq1jaT0s5pC0iFD4+BOAA=
+github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48 h1:iZOop7pqsg+56twTopWgwCGxdB5SI2yDO8Ti7eTRliQ=
+github.com/dop251/goja v0.0.0-20211011172007-d99e4b8cbf48/go.mod h1:R9ET47fwRVRPZnOGvHxxhuZcbrMCuiqOz3Rlrh4KSnk=
+github.com/dop251/goja_nodejs v0.0.0-20210225215109-d91c329300e7/go.mod h1:hn7BA7c8pLvoGndExHudxTDKZ84Pyvv+90pbBjbTz0Y=
github.com/eclipse/paho.mqtt.golang v1.2.0/go.mod h1:H9keYFcgq3Qr5OUJm/JZI/i6U7joQ8SYLhZwfeOo6Ts=
github.com/edsrzf/mmap-go v1.0.0 h1:CEBF7HpRnUCSJgGUb5h1Gm7e3VkmVDrR8lvWVLtrOFw=
github.com/edsrzf/mmap-go v1.0.0/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
@@ -157,8 +159,8 @@ github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
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.2+incompatible h1:0b/xya7BKGhXuqFESKM4oIiRo9WOt2ebz7KxfreD6ug=
-github.com/go-sourcemap/sourcemap v2.1.2+incompatible/go.mod h1:F8jJfvm2KbVjc5NqelyYJmf/v5J0dwNLS2mL4sNA1Jg=
+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-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
@@ -185,9 +187,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM=
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
+github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
+github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golangci/lint-1 v0.0.0-20181222135242-d2cdd8c08219/go.mod h1:/X8TswGSh1pIozq4ZwCfxS0WA5JGXguxk94ar/4c87Y=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
@@ -216,6 +218,8 @@ github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0U
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29 h1:sezaKhEfPFg8W0Enm61B9Gs911H8iesGY5R8NDPtd1M=
github.com/graph-gophers/graphql-go v0.0.0-20201113091052-beb923fada29/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc=
+github.com/hashicorp/go-bexpr v0.1.10 h1:9kuI5PFotCboP3dkDYFr/wi0gg0QVbSNz5oFRpxn4uE=
+github.com/hashicorp/go-bexpr v0.1.10/go.mod h1:oxlubA2vC/gFVfX1A6JGp7ls7uCDlfJn732ehYYg+g0=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
github.com/hashicorp/golang-lru v0.5.5-0.20210104140557-80c98217689d h1:dg1dEPuWpEqDnvIw251EVy4zlP8gWbsGj4BsUKCRpYs=
@@ -261,8 +265,8 @@ github.com/julienschmidt/httprouter v1.2.0 h1:TDTW5Yz1mjftljbcKqRcrYhd4XeOoI98t+
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jwilder/encoding v0.0.0-20170811194829-b4e1701a28ef/go.mod h1:Ct9fl0F6iIOGgxJ5npU/IUOhOhqlVrGjyIZc8/MagT0=
-github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356 h1:I/yrLt2WilKxlQKCM52clh5rGzTKpVctGT1lH4Dc8Jw=
-github.com/karalabe/usb v0.0.0-20190919080040-51dc0efba356/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
+github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559 h1:0VWDXPNE0brOek1Q8bLfzKkvOzwbQE/snjGojlCr8CY=
+github.com/karalabe/usb v0.0.0-20211005121534-4c5740d64559/go.mod h1:Od972xHfMJowv7NGVDiWVxk2zxnWgjLlJzE+F4F7AGU=
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4=
@@ -273,11 +277,13 @@ github.com/klauspost/pgzip v1.0.2-0.20170402124221-0bf5dcad4ada/go.mod h1:Ch1tH6
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
+github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
+github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+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/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4FW1e6jwpg=
@@ -307,6 +313,10 @@ github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m
github.com/mattn/go-sqlite3 v1.11.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/mattn/go-tty v0.0.0-20180907095812-13ff1204f104/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
+github.com/mitchellh/mapstructure v1.4.1 h1:CpVNEelQCZBooIPDn+AR3NpivK/TIKU8bDxdASFVQag=
+github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
+github.com/mitchellh/pointerstructure v1.2.0 h1:O+i9nHnXS3l/9Wu7r4NrEdwA2VFTicjUEN1uBnDo34A=
+github.com/mitchellh/pointerstructure v1.2.0/go.mod h1:BRAsLI5zgXmw97Lf6s25bs8ohIXc3tViBH44KcwB2g4=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/mschoch/smat v0.0.0-20160514031455-90eadee771ae/go.mod h1:qAyveg+e4CE+eKJXWVjKXM4ck2QobLqTDytGJbLLhJg=
@@ -389,8 +399,8 @@ github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81P
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954 h1:xQdMZ1WLrgkkvOZ/LDQxjVxMLdby7osSh4ZEVa5sIjs=
-github.com/syndtr/goleveldb v1.0.1-0.20210305035536-64b5b1c73954/go.mod h1:u2MKkTVTVJWe5D1rCvame8WqhBd88EuIwODJZ1VHCPM=
+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/tinylib/msgp v1.0.2/go.mod h1:+d+yLhGm8mzTaHzB+wgMYrodPfmZrzkirds8fDWklFE=
github.com/tklauser/go-sysconf v0.3.5 h1:uu3Xl4nkLzQfXNsWn15rPc/HQCJKObbt1dKJeWp3vU4=
github.com/tklauser/go-sysconf v0.3.5/go.mod h1:MkWzOF4RMCshBAMXuhXJs64Rte09mITnppBXY/rYEFI=
@@ -622,8 +632,9 @@ google.golang.org/protobuf v1.23.0 h1:4MY060fB1DLGMB/7MBTLnwQUY6+F09GEiz6SsrNqyz
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
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/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce h1:+JknDZhAj8YMt7GC73Ei8pv4MzjDUNPHgQWJdtMAaDU=
diff --git a/graphql/graphql.go b/graphql/graphql.go
index d35994234e..af6b7dc317 100644
--- a/graphql/graphql.go
+++ b/graphql/graphql.go
@@ -23,7 +23,6 @@ import (
"fmt"
"math/big"
"strconv"
- "time"
"github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/common"
@@ -158,14 +157,14 @@ func (l *Log) Data(ctx context.Context) hexutil.Bytes {
// AccessTuple represents EIP-2930
type AccessTuple struct {
address common.Address
- storageKeys *[]common.Hash
+ storageKeys []common.Hash
}
func (at *AccessTuple) Address(ctx context.Context) common.Address {
return at.address
}
-func (at *AccessTuple) StorageKeys(ctx context.Context) *[]common.Hash {
+func (at *AccessTuple) StorageKeys(ctx context.Context) []common.Hash {
return at.storageKeys
}
@@ -442,7 +441,7 @@ func (t *Transaction) AccessList(ctx context.Context) (*[]*AccessTuple, error) {
for _, al := range accessList {
ret = append(ret, &AccessTuple{
address: al.Address,
- storageKeys: &al.StorageKeys,
+ storageKeys: al.StorageKeys,
})
}
return &ret, nil
@@ -954,7 +953,7 @@ func (b *Block) Call(ctx context.Context, args struct {
return nil, err
}
}
- result, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, 5*time.Second, b.backend.RPCGasCap())
+ result, err := ethapi.DoCall(ctx, b.backend, args.Data, *b.numberOrHash, nil, b.backend.RPCEVMTimeout(), b.backend.RPCGasCap())
if err != nil {
return nil, err
}
@@ -1024,7 +1023,7 @@ func (p *Pending) Call(ctx context.Context, args struct {
Data ethapi.TransactionArgs
}) (*CallResult, error) {
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
- result, err := ethapi.DoCall(ctx, p.backend, args.Data, pendingBlockNr, nil, 5*time.Second, p.backend.RPCGasCap())
+ result, err := ethapi.DoCall(ctx, p.backend, args.Data, pendingBlockNr, nil, p.backend.RPCEVMTimeout(), p.backend.RPCGasCap())
if err != nil {
return nil, err
}
@@ -1248,7 +1247,7 @@ func (s *SyncState) KnownStates() *hexutil.Uint64 {
// - pulledStates: number of state entries processed until now
// - knownStates: number of known state entries that still need to be pulled
func (r *Resolver) Syncing() (*SyncState, error) {
- progress := r.backend.Downloader().Progress()
+ progress := r.backend.SyncProgress()
// Return not syncing if the synchronisation already completed
if progress.CurrentBlock >= progress.HighestBlock {
diff --git a/graphql/schema.go b/graphql/schema.go
index 811c11f6cd..dfd094a420 100644
--- a/graphql/schema.go
+++ b/graphql/schema.go
@@ -72,7 +72,7 @@ const schema string = `
#EIP-2718
type AccessTuple{
address: Address!
- storageKeys : [Bytes32!]
+ storageKeys : [Bytes32!]!
}
# Transaction is an Ethereum transaction.
diff --git a/internal/cmdtest/test_cmd.go b/internal/cmdtest/test_cmd.go
index 82ad9c15b6..d75db41e0d 100644
--- a/internal/cmdtest/test_cmd.go
+++ b/internal/cmdtest/test_cmd.go
@@ -118,6 +118,13 @@ func (tt *TestCmd) Expect(tplsource string) {
tt.Logf("Matched stdout text:\n%s", want)
}
+// Output reads all output from stdout, and returns the data.
+func (tt *TestCmd) Output() []byte {
+ var buf []byte
+ tt.withKillTimeout(func() { buf, _ = io.ReadAll(tt.stdout) })
+ return buf
+}
+
func (tt *TestCmd) matchExactOutput(want []byte) error {
buf := make([]byte, len(want))
n := 0
diff --git a/internal/debug/api.go b/internal/debug/api.go
index efd8626776..1ea0c6377f 100644
--- a/internal/debug/api.go
+++ b/internal/debug/api.go
@@ -27,6 +27,7 @@ import (
"os"
"os/user"
"path/filepath"
+ "regexp"
"runtime"
"runtime/debug"
"runtime/pprof"
@@ -35,6 +36,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/log"
+ "github.com/hashicorp/go-bexpr"
)
// Handler is the global debugging handler.
@@ -189,10 +191,44 @@ func (*HandlerT) WriteMemProfile(file string) error {
return writeProfile("heap", file)
}
-// Stacks returns a printed representation of the stacks of all goroutines.
-func (*HandlerT) Stacks() string {
+// Stacks returns a printed representation of the stacks of all goroutines. It
+// also permits the following optional filters to be used:
+// - filter: boolean expression of packages to filter for
+func (*HandlerT) Stacks(filter *string) string {
buf := new(bytes.Buffer)
pprof.Lookup("goroutine").WriteTo(buf, 2)
+
+ // If any filtering was requested, execute them now
+ if filter != nil && len(*filter) > 0 {
+ expanded := *filter
+
+ // The input filter is a logical expression of package names. Transform
+ // it into a proper boolean expression that can be fed into a parser and
+ // interpreter:
+ //
+ // E.g. (eth || snap) && !p2p -> (eth in Value || snap in Value) && p2p not in Value
+ expanded = regexp.MustCompile(`[:/\.A-Za-z0-9_-]+`).ReplaceAllString(expanded, "`$0` in Value")
+ expanded = regexp.MustCompile("!(`[:/\\.A-Za-z0-9_-]+`)").ReplaceAllString(expanded, "$1 not")
+ expanded = strings.Replace(expanded, "||", "or", -1)
+ expanded = strings.Replace(expanded, "&&", "and", -1)
+ log.Info("Expanded filter expression", "filter", *filter, "expanded", expanded)
+
+ expr, err := bexpr.CreateEvaluator(expanded)
+ if err != nil {
+ log.Error("Failed to parse filter expression", "expanded", expanded, "err", err)
+ return ""
+ }
+ // Split the goroutine dump into segments and filter each
+ dump := buf.String()
+ buf.Reset()
+
+ for _, trace := range strings.Split(dump, "\n\n") {
+ if ok, _ := expr.Evaluate(map[string]string{"Value": trace}); ok {
+ buf.WriteString(trace)
+ buf.WriteString("\n\n")
+ }
+ }
+ }
return buf.String()
}
diff --git a/internal/debug/flags.go b/internal/debug/flags.go
index 126ee09a7c..3aa990adfb 100644
--- a/internal/debug/flags.go
+++ b/internal/debug/flags.go
@@ -90,39 +90,6 @@ var (
Name: "trace",
Usage: "Write execution trace to the given file",
}
- // (Deprecated April 2020)
- legacyPprofPortFlag = cli.IntFlag{
- Name: "pprofport",
- Usage: "pprof HTTP server listening port (deprecated, use --pprof.port)",
- Value: 6060,
- }
- legacyPprofAddrFlag = cli.StringFlag{
- Name: "pprofaddr",
- Usage: "pprof HTTP server listening interface (deprecated, use --pprof.addr)",
- Value: "127.0.0.1",
- }
- legacyMemprofilerateFlag = cli.IntFlag{
- Name: "memprofilerate",
- Usage: "Turn on memory profiling with the given rate (deprecated, use --pprof.memprofilerate)",
- Value: runtime.MemProfileRate,
- }
- legacyBlockprofilerateFlag = cli.IntFlag{
- Name: "blockprofilerate",
- Usage: "Turn on block profiling with the given rate (deprecated, use --pprof.blockprofilerate)",
- }
- legacyCpuprofileFlag = cli.StringFlag{
- Name: "cpuprofile",
- Usage: "Write CPU profile to the given file (deprecated, use --pprof.cpuprofile)",
- }
- legacyBacktraceAtFlag = cli.StringFlag{
- Name: "backtrace",
- Usage: "Request a stack trace at a specific logging statement (e.g. \"block.go:271\") (deprecated, use --log.backtrace)",
- Value: "",
- }
- legacyDebugFlag = cli.BoolFlag{
- Name: "debug",
- Usage: "Prepends log messages with call-site location (file and line number) (deprecated, use --log.debug)",
- }
)
// Flags holds all command-line flags required for debugging.
@@ -141,17 +108,6 @@ var Flags = []cli.Flag{
traceFlag,
}
-// This is the list of deprecated debugging flags.
-var DeprecatedFlags = []cli.Flag{
- legacyPprofPortFlag,
- legacyPprofAddrFlag,
- legacyMemprofilerateFlag,
- legacyBlockprofilerateFlag,
- legacyCpuprofileFlag,
- legacyBacktraceAtFlag,
- legacyDebugFlag,
-}
-
var glogger *log.GlogHandler
func init() {
@@ -183,45 +139,23 @@ func Setup(ctx *cli.Context) error {
glogger.Vmodule(vmodule)
debug := ctx.GlobalBool(debugFlag.Name)
- if ctx.GlobalIsSet(legacyDebugFlag.Name) {
- debug = ctx.GlobalBool(legacyDebugFlag.Name)
- log.Warn("The flag --debug is deprecated and will be removed in the future, please use --log.debug")
- }
if ctx.GlobalIsSet(debugFlag.Name) {
debug = ctx.GlobalBool(debugFlag.Name)
}
log.PrintOrigins(debug)
backtrace := ctx.GlobalString(backtraceAtFlag.Name)
- if b := ctx.GlobalString(legacyBacktraceAtFlag.Name); b != "" {
- backtrace = b
- log.Warn("The flag --backtrace is deprecated and will be removed in the future, please use --log.backtrace")
- }
- if b := ctx.GlobalString(backtraceAtFlag.Name); b != "" {
- backtrace = b
- }
glogger.BacktraceAt(backtrace)
log.Root().SetHandler(glogger)
// profiling, tracing
runtime.MemProfileRate = memprofilerateFlag.Value
- if ctx.GlobalIsSet(legacyMemprofilerateFlag.Name) {
- runtime.MemProfileRate = ctx.GlobalInt(legacyMemprofilerateFlag.Name)
- log.Warn("The flag --memprofilerate is deprecated and will be removed in the future, please use --pprof.memprofilerate")
- }
if ctx.GlobalIsSet(memprofilerateFlag.Name) {
runtime.MemProfileRate = ctx.GlobalInt(memprofilerateFlag.Name)
}
- blockProfileRate := blockprofilerateFlag.Value
- if ctx.GlobalIsSet(legacyBlockprofilerateFlag.Name) {
- blockProfileRate = ctx.GlobalInt(legacyBlockprofilerateFlag.Name)
- log.Warn("The flag --blockprofilerate is deprecated and will be removed in the future, please use --pprof.blockprofilerate")
- }
- if ctx.GlobalIsSet(blockprofilerateFlag.Name) {
- blockProfileRate = ctx.GlobalInt(blockprofilerateFlag.Name)
- }
+ blockProfileRate := ctx.GlobalInt(blockprofilerateFlag.Name)
Handler.SetBlockProfileRate(blockProfileRate)
if traceFile := ctx.GlobalString(traceFlag.Name); traceFile != "" {
diff --git a/internal/debug/loudpanic.go b/internal/debug/loudpanic.go
index 572ebcefa1..86e6bc88f8 100644
--- a/internal/debug/loudpanic.go
+++ b/internal/debug/loudpanic.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 go1.6
// +build go1.6
package debug
diff --git a/internal/debug/loudpanic_fallback.go b/internal/debug/loudpanic_fallback.go
index 4ce4985da7..377490e5be 100644
--- a/internal/debug/loudpanic_fallback.go
+++ b/internal/debug/loudpanic_fallback.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 !go1.6
// +build !go1.6
package debug
diff --git a/internal/debug/trace.go b/internal/debug/trace.go
index cab5deaafd..a273e4a9db 100644
--- a/internal/debug/trace.go
+++ b/internal/debug/trace.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 go1.5
+//go:build go1.5
+// +build go1.5
package debug
diff --git a/internal/debug/trace_fallback.go b/internal/debug/trace_fallback.go
index 4118ff4087..ec07d991ef 100644
--- a/internal/debug/trace_fallback.go
+++ b/internal/debug/trace_fallback.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 !go1.5
+//go:build !go1.5
+// +build !go1.5
// no-op implementation of tracing methods for Go < 1.5.
diff --git a/internal/ethapi/api.go b/internal/ethapi/api.go
index 1af98e1071..aa8e8767c3 100644
--- a/internal/ethapi/api.go
+++ b/internal/ethapi/api.go
@@ -32,7 +32,6 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/common/math"
- "github.com/ethereum/go-ethereum/consensus"
"github.com/ethereum/go-ethereum/consensus/clique"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/consensus/misc"
@@ -123,7 +122,7 @@ func (s *PublicEthereumAPI) FeeHistory(ctx context.Context, blockCount rpc.Decim
// - pulledStates: number of state entries processed until now
// - knownStates: number of known state entries that still need to be pulled
func (s *PublicEthereumAPI) Syncing() (interface{}, error) {
- progress := s.b.Downloader().Progress()
+ progress := s.b.SyncProgress()
// Return not syncing if the synchronisation already completed
if progress.CurrentBlock >= progress.HighestBlock {
@@ -973,7 +972,7 @@ func (e *revertError) ErrorData() interface{} {
// Note, this function doesn't make and changes in the state/blockchain and is
// useful to execute and retrieve values.
func (s *PublicBlockChainAPI) Call(ctx context.Context, args TransactionArgs, blockNrOrHash rpc.BlockNumberOrHash, overrides *StateOverride) (hexutil.Bytes, error) {
- result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, 5*time.Second, s.b.RPCGasCap())
+ result, err := DoCall(ctx, s.b, args, blockNrOrHash, overrides, s.b.RPCEVMTimeout(), s.b.RPCGasCap())
if err != nil {
return nil, err
}
@@ -1176,8 +1175,7 @@ func FormatLogs(logs []vm.StructLog) []StructLogRes {
}
// RPCMarshalHeader converts the given header to the RPC output .
-func RPCMarshalHeader(head *types.Header, engine consensus.Engine) map[string]interface{} {
- miner, _ := engine.Author(head)
+func RPCMarshalHeader(head *types.Header) map[string]interface{} {
result := map[string]interface{}{
"number": (*hexutil.Big)(head.Number),
"hash": head.Hash(),
@@ -1187,7 +1185,7 @@ func RPCMarshalHeader(head *types.Header, engine consensus.Engine) map[string]in
"sha3Uncles": head.UncleHash,
"logsBloom": head.Bloom,
"stateRoot": head.Root,
- "miner": miner,
+ "miner": head.Coinbase,
"difficulty": (*hexutil.Big)(head.Difficulty),
"extraData": hexutil.Bytes(head.Extra),
"size": hexutil.Uint64(head.Size()),
@@ -1208,8 +1206,8 @@ func RPCMarshalHeader(head *types.Header, engine consensus.Engine) map[string]in
// RPCMarshalBlock 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 RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, engine consensus.Engine) (map[string]interface{}, error) {
- fields := RPCMarshalHeader(block.Header(), engine)
+func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, config *params.ChainConfig) (map[string]interface{}, error) {
+ fields := RPCMarshalHeader(block.Header())
fields["size"] = hexutil.Uint64(block.Size())
if inclTx {
@@ -1218,7 +1216,7 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, engine consen
}
if fullTx {
formatTx = func(tx *types.Transaction) (interface{}, error) {
- return newRPCTransactionFromBlockHash(block, tx.Hash()), nil
+ return newRPCTransactionFromBlockHash(block, tx.Hash(), config), nil
}
}
txs := block.Transactions()
@@ -1244,7 +1242,7 @@ func RPCMarshalBlock(block *types.Block, inclTx bool, fullTx bool, engine consen
// rpcMarshalHeader uses the generalized output filler, then adds the total difficulty field, which requires
// a `PublicBlockchainAPI`.
func (s *PublicBlockChainAPI) rpcMarshalHeader(ctx context.Context, header *types.Header) map[string]interface{} {
- fields := RPCMarshalHeader(header, s.b.Engine())
+ fields := RPCMarshalHeader(header)
fields["totalDifficulty"] = (*hexutil.Big)(s.b.GetTd(ctx, header.Hash()))
return fields
}
@@ -1252,7 +1250,7 @@ func (s *PublicBlockChainAPI) rpcMarshalHeader(ctx context.Context, header *type
// rpcMarshalBlock uses the generalized output filler, then adds the total difficulty field, which requires
// a `PublicBlockchainAPI`.
func (s *PublicBlockChainAPI) rpcMarshalBlock(ctx context.Context, b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) {
- fields, err := RPCMarshalBlock(b, inclTx, fullTx, s.b.Engine())
+ fields, err := RPCMarshalBlock(b, inclTx, fullTx, s.b.ChainConfig())
if err != nil {
return nil, err
}
@@ -1287,17 +1285,8 @@ 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, 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
- // because the return value of ChainId is zero for those transactions.
- var signer types.Signer
- if tx.Protected() {
- signer = types.LatestSignerForChainID(tx.ChainId())
- } else {
- signer = types.HomesteadSigner{}
- }
+func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber uint64, index uint64, baseFee *big.Int, config *params.ChainConfig) *RPCTransaction {
+ signer := types.MakeSigner(config, big.NewInt(0).SetUint64(blockNumber))
from, _ := types.Sender(signer, tx)
v, r, s := tx.RawSignatureValues()
result := &RPCTransaction{
@@ -1345,19 +1334,21 @@ func newRPCTransaction(tx *types.Transaction, blockHash common.Hash, blockNumber
// newRPCPendingTransaction returns a pending transaction that will serialize to the RPC representation
func newRPCPendingTransaction(tx *types.Transaction, current *types.Header, config *params.ChainConfig) *RPCTransaction {
var baseFee *big.Int
+ blockNumber := uint64(0)
if current != nil {
baseFee = misc.CalcBaseFee(config, current)
+ blockNumber = current.Number.Uint64()
}
- return newRPCTransaction(tx, common.Hash{}, 0, 0, baseFee)
+ return newRPCTransaction(tx, common.Hash{}, blockNumber, 0, baseFee, config)
}
// newRPCTransactionFromBlockIndex returns a transaction that will serialize to the RPC representation.
-func newRPCTransactionFromBlockIndex(b *types.Block, index uint64) *RPCTransaction {
+func newRPCTransactionFromBlockIndex(b *types.Block, index uint64, config *params.ChainConfig) *RPCTransaction {
txs := b.Transactions()
if index >= uint64(len(txs)) {
return nil
}
- return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index, b.BaseFee())
+ return newRPCTransaction(txs[index], b.Hash(), b.NumberU64(), index, b.BaseFee(), config)
}
// newRPCRawTransactionFromBlockIndex returns the bytes of a transaction given a block and a transaction index.
@@ -1371,10 +1362,10 @@ func newRPCRawTransactionFromBlockIndex(b *types.Block, index uint64) hexutil.By
}
// newRPCTransactionFromBlockHash returns a transaction that will serialize to the RPC representation.
-func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash) *RPCTransaction {
+func newRPCTransactionFromBlockHash(b *types.Block, hash common.Hash, config *params.ChainConfig) *RPCTransaction {
for idx, tx := range b.Transactions() {
if tx.Hash() == hash {
- return newRPCTransactionFromBlockIndex(b, uint64(idx))
+ return newRPCTransactionFromBlockIndex(b, uint64(idx), config)
}
}
return nil
@@ -1515,7 +1506,7 @@ func (s *PublicTransactionPoolAPI) GetBlockTransactionCountByHash(ctx context.Co
// GetTransactionByBlockNumberAndIndex returns the transaction for the given block number and index.
func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx context.Context, blockNr rpc.BlockNumber, index hexutil.Uint) *RPCTransaction {
if block, _ := s.b.BlockByNumber(ctx, blockNr); block != nil {
- return newRPCTransactionFromBlockIndex(block, uint64(index))
+ return newRPCTransactionFromBlockIndex(block, uint64(index), s.b.ChainConfig())
}
return nil
}
@@ -1523,7 +1514,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionByBlockNumberAndIndex(ctx conte
// GetTransactionByBlockHashAndIndex returns the transaction for the given block hash and index.
func (s *PublicTransactionPoolAPI) GetTransactionByBlockHashAndIndex(ctx context.Context, blockHash common.Hash, index hexutil.Uint) *RPCTransaction {
if block, _ := s.b.BlockByHash(ctx, blockHash); block != nil {
- return newRPCTransactionFromBlockIndex(block, uint64(index))
+ return newRPCTransactionFromBlockIndex(block, uint64(index), s.b.ChainConfig())
}
return nil
}
@@ -1575,7 +1566,7 @@ func (s *PublicTransactionPoolAPI) GetTransactionByHash(ctx context.Context, has
if err != nil {
return nil, err
}
- return newRPCTransaction(tx, blockHash, blockNumber, index, header.BaseFee), nil
+ return newRPCTransaction(tx, blockHash, blockNumber, index, header.BaseFee, s.b.ChainConfig()), nil
}
// No finalized transaction, try to retrieve it from the pool
if tx := s.b.GetPoolTransaction(hash); tx != nil {
@@ -1917,17 +1908,22 @@ func NewPublicDebugAPI(b Backend) *PublicDebugAPI {
return &PublicDebugAPI{b: b}
}
+// GetHeaderRlp retrieves the RLP encoded for of a single header.
+func (api *PublicDebugAPI) GetHeaderRlp(ctx context.Context, number uint64) (hexutil.Bytes, error) {
+ header, _ := api.b.HeaderByNumber(ctx, rpc.BlockNumber(number))
+ if header == nil {
+ return nil, fmt.Errorf("header #%d not found", number)
+ }
+ return rlp.EncodeToBytes(header)
+}
+
// GetBlockRlp retrieves the RLP encoded for of a single block.
-func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number uint64) (string, error) {
+func (api *PublicDebugAPI) GetBlockRlp(ctx context.Context, number uint64) (hexutil.Bytes, error) {
block, _ := api.b.BlockByNumber(ctx, rpc.BlockNumber(number))
if block == nil {
- return "", fmt.Errorf("block #%d not found", number)
+ return nil, fmt.Errorf("block #%d not found", number)
}
- encoded, err := rlp.EncodeToBytes(block)
- if err != nil {
- return "", err
- }
- return fmt.Sprintf("%x", encoded), nil
+ return rlp.EncodeToBytes(block)
}
// TestSignCliqueBlock fetches the given block number, and attempts to sign it as a clique header with the
diff --git a/internal/ethapi/backend.go b/internal/ethapi/backend.go
index 9954545821..bc60fb2a64 100644
--- a/internal/ethapi/backend.go
+++ b/internal/ethapi/backend.go
@@ -20,7 +20,9 @@ package ethapi
import (
"context"
"math/big"
+ "time"
+ "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
@@ -29,7 +31,6 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/params"
@@ -40,15 +41,17 @@ import (
// both full and light clients) with access to necessary functions.
type Backend interface {
// General Ethereum API
- Downloader() *downloader.Downloader
+ SyncProgress() ethereum.SyncProgress
+
SuggestGasTipCap(ctx context.Context) (*big.Int, error)
FeeHistory(ctx context.Context, blockCount int, lastBlock rpc.BlockNumber, rewardPercentiles []float64) (*big.Int, [][]*big.Int, []*big.Int, []float64, error)
ChainDb() ethdb.Database
AccountManager() *accounts.Manager
ExtRPCEnabled() bool
- RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
- RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs
- UnprotectedAllowed() bool // allows only for EIP155 transactions.
+ RPCGasCap() uint64 // global gas cap for eth_call over rpc: DoS protection
+ RPCEVMTimeout() time.Duration // global timeout for eth_call over rpc: DoS protection
+ RPCTxFeeCap() float64 // global tx fee cap for all transaction related APIs
+ UnprotectedAllowed() bool // allows only for EIP155 transactions.
// Blockchain API
SetHead(number uint64)
diff --git a/internal/ethapi/transaction_args.go b/internal/ethapi/transaction_args.go
index 52811b2a9a..2d08d3008f 100644
--- a/internal/ethapi/transaction_args.go
+++ b/internal/ethapi/transaction_args.go
@@ -146,6 +146,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error {
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,
@@ -153,7 +154,7 @@ func (args *TransactionArgs) setDefaults(ctx context.Context, b Backend) error {
MaxFeePerGas: args.MaxFeePerGas,
MaxPriorityFeePerGas: args.MaxPriorityFeePerGas,
Value: args.Value,
- Data: args.Data,
+ Data: (*hexutil.Bytes)(&data),
AccessList: args.AccessList,
}
pendingBlockNr := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
diff --git a/internal/jsre/completion.go b/internal/jsre/completion.go
index 2c105184cb..538fca298d 100644
--- a/internal/jsre/completion.go
+++ b/internal/jsre/completion.go
@@ -44,7 +44,7 @@ func getCompletions(vm *goja.Runtime, line string) (results []string) {
obj := vm.GlobalObject()
for i := 0; i < len(parts)-1; i++ {
v := obj.Get(parts[i])
- if v == nil {
+ if v == nil || goja.IsNull(v) || goja.IsUndefined(v) {
return nil // No object was found
}
obj = v.ToObject(vm)
diff --git a/internal/jsre/completion_test.go b/internal/jsre/completion_test.go
index 2d05547d12..953bc5026d 100644
--- a/internal/jsre/completion_test.go
+++ b/internal/jsre/completion_test.go
@@ -72,6 +72,7 @@ func TestCompleteKeywords(t *testing.T) {
{
input: "x.gazonk.",
want: []string{
+ "x.gazonk.__proto__",
"x.gazonk.constructor",
"x.gazonk.hasOwnProperty",
"x.gazonk.isPrototypeOf",
diff --git a/internal/jsre/deps/bignumber.js b/internal/jsre/deps/bignumber.js
index 17c8851e24..aadb182ae1 100644
--- a/internal/jsre/deps/bignumber.js
+++ b/internal/jsre/deps/bignumber.js
@@ -1,4 +1,4 @@
/* bignumber.js v2.0.3 https://github.com/MikeMcl/bignumber.js/LICENCE */
/* modified by zelig to fix https://github.com/robertkrimen/otto#regular-expression-incompatibility */
!function(e){"use strict";function n(e){function a(e,n){var t,r,i,o,u,s,f=this;if(!(f instanceof a))return j&&L(26,"constructor call without new",e),new a(e,n);if(null!=n&&H(n,2,64,M,"base")){if(n=0|n,s=e+"",10==n)return f=new a(e instanceof a?e:s),U(f,P+f.e+1,k);if((o="number"==typeof e)&&0*e!=0||!new RegExp("^-?"+(t="["+O.slice(0,n)+"]+")+"(?:\\."+t+")?$",37>n?"i":"").test(s))return g(f,s,o,n);o?(f.s=0>1/e?(s=s.slice(1),-1):1,j&&s.replace(/^0\.0*|\./,"").length>15&&L(M,b,e),o=!1):f.s=45===s.charCodeAt(0)?(s=s.slice(1),-1):1,s=D(s,10,n,f.s)}else{if(e instanceof a)return f.s=e.s,f.e=e.e,f.c=(e=e.c)?e.slice():e,void(M=0);if((o="number"==typeof e)&&0*e==0){if(f.s=0>1/e?(e=-e,-1):1,e===~~e){for(r=0,i=e;i>=10;i/=10,r++);return f.e=r,f.c=[e],void(M=0)}s=e+""}else{if(!p.test(s=e+""))return g(f,s,o);f.s=45===s.charCodeAt(0)?(s=s.slice(1),-1):1}}for((r=s.indexOf("."))>-1&&(s=s.replace(".","")),(i=s.search(/e/i))>0?(0>r&&(r=i),r+=+s.slice(i+1),s=s.substring(0,i)):0>r&&(r=s.length),i=0;48===s.charCodeAt(i);i++);for(u=s.length;48===s.charCodeAt(--u););if(s=s.slice(i,u+1))if(u=s.length,o&&j&&u>15&&L(M,b,f.s*e),r=r-i-1,r>z)f.c=f.e=null;else if(G>r)f.c=[f.e=0];else{if(f.e=r,f.c=[],i=(r+1)%y,0>r&&(i+=y),u>i){for(i&&f.c.push(+s.slice(0,i)),u-=y;u>i;)f.c.push(+s.slice(i,i+=y));s=s.slice(i),i=y-s.length}else i-=u;for(;i--;s+="0");f.c.push(+s)}else f.c=[f.e=0];M=0}function D(e,n,t,i){var o,u,f,c,h,g,p,d=e.indexOf("."),m=P,w=k;for(37>t&&(e=e.toLowerCase()),d>=0&&(f=J,J=0,e=e.replace(".",""),p=new a(t),h=p.pow(e.length-d),J=f,p.c=s(l(r(h.c),h.e),10,n),p.e=p.c.length),g=s(e,t,n),u=f=g.length;0==g[--f];g.pop());if(!g[0])return"0";if(0>d?--u:(h.c=g,h.e=u,h.s=i,h=C(h,p,m,w,n),g=h.c,c=h.r,u=h.e),o=u+m+1,d=g[o],f=n/2,c=c||0>o||null!=g[o+1],c=4>w?(null!=d||c)&&(0==w||w==(h.s<0?3:2)):d>f||d==f&&(4==w||c||6==w&&1&g[o-1]||w==(h.s<0?8:7)),1>o||!g[0])e=c?l("1",-m):"0";else{if(g.length=o,c)for(--n;++g[--o]>n;)g[o]=0,o||(++u,g.unshift(1));for(f=g.length;!g[--f];);for(d=0,e="";f>=d;e+=O.charAt(g[d++]));e=l(e,u)}return e}function _(e,n,t,i){var o,u,s,c,h;if(t=null!=t&&H(t,0,8,i,v)?0|t:k,!e.c)return e.toString();if(o=e.c[0],s=e.e,null==n)h=r(e.c),h=19==i||24==i&&B>=s?f(h,s):l(h,s);else if(e=U(new a(e),n,t),u=e.e,h=r(e.c),c=h.length,19==i||24==i&&(u>=n||B>=u)){for(;n>c;h+="0",c++);h=f(h,u)}else if(n-=s,h=l(h,u),u+1>c){if(--n>0)for(h+=".";n--;h+="0");}else if(n+=u-c,n>0)for(u+1==c&&(h+=".");n--;h+="0");return e.s<0&&o?"-"+h:h}function x(e,n){var t,r,i=0;for(u(e[0])&&(e=e[0]),t=new a(e[0]);++ie||e>t||e!=c(e))&&L(r,(i||"decimal places")+(n>e||e>t?" out of range":" not an integer"),e),!0}function I(e,n,t){for(var r=1,i=n.length;!n[--i];n.pop());for(i=n[0];i>=10;i/=10,r++);return(t=r+t*y-1)>z?e.c=e.e=null:G>t?e.c=[e.e=0]:(e.e=t,e.c=n),e}function L(e,n,t){var r=new Error(["new BigNumber","cmp","config","div","divToInt","eq","gt","gte","lt","lte","minus","mod","plus","precision","random","round","shift","times","toDigits","toExponential","toFixed","toFormat","toFraction","pow","toPrecision","toString","BigNumber"][e]+"() "+n+": "+t);throw r.name="BigNumber Error",M=0,r}function U(e,n,t,r){var i,o,u,s,f,l,c,a=e.c,h=R;if(a){e:{for(i=1,s=a[0];s>=10;s/=10,i++);if(o=n-i,0>o)o+=y,u=n,f=a[l=0],c=f/h[i-u-1]%10|0;else if(l=d((o+1)/y),l>=a.length){if(!r)break e;for(;a.length<=l;a.push(0));f=c=0,i=1,o%=y,u=o-y+1}else{for(f=s=a[l],i=1;s>=10;s/=10,i++);o%=y,u=o-y+i,c=0>u?0:f/h[i-u-1]%10|0}if(r=r||0>n||null!=a[l+1]||(0>u?f:f%h[i-u-1]),r=4>t?(c||r)&&(0==t||t==(e.s<0?3:2)):c>5||5==c&&(4==t||r||6==t&&(o>0?u>0?f/h[i-u]:0:a[l-1])%10&1||t==(e.s<0?8:7)),1>n||!a[0])return a.length=0,r?(n-=e.e+1,a[0]=h[n%y],e.e=-n||0):a[0]=e.e=0,e;if(0==o?(a.length=l,s=1,l--):(a.length=l+1,s=h[y-o],a[l]=u>0?m(f/h[i-u]%h[u])*s:0),r)for(;;){if(0==l){for(o=1,u=a[0];u>=10;u/=10,o++);for(u=a[0]+=s,s=1;u>=10;u/=10,s++);o!=s&&(e.e++,a[0]==N&&(a[0]=1));break}if(a[l]+=s,a[l]!=N)break;a[l--]=0,s=1}for(o=a.length;0===a[--o];a.pop());}e.e>z?e.c=e.e=null:e.et?null!=(e=i[t++]):void 0};return f(n="DECIMAL_PLACES")&&H(e,0,E,2,n)&&(P=0|e),r[n]=P,f(n="ROUNDING_MODE")&&H(e,0,8,2,n)&&(k=0|e),r[n]=k,f(n="EXPONENTIAL_AT")&&(u(e)?H(e[0],-E,0,2,n)&&H(e[1],0,E,2,n)&&(B=0|e[0],$=0|e[1]):H(e,-E,E,2,n)&&(B=-($=0|(0>e?-e:e)))),r[n]=[B,$],f(n="RANGE")&&(u(e)?H(e[0],-E,-1,2,n)&&H(e[1],1,E,2,n)&&(G=0|e[0],z=0|e[1]):H(e,-E,E,2,n)&&(0|e?G=-(z=0|(0>e?-e:e)):j&&L(2,n+" cannot be zero",e))),r[n]=[G,z],f(n="ERRORS")&&(e===!!e||1===e||0===e?(M=0,H=(j=!!e)?F:o):j&&L(2,n+w,e)),r[n]=j,f(n="CRYPTO")&&(e===!!e||1===e||0===e?(V=!(!e||!h||"object"!=typeof h),e&&!V&&j&&L(2,"crypto unavailable",h)):j&&L(2,n+w,e)),r[n]=V,f(n="MODULO_MODE")&&H(e,0,9,2,n)&&(W=0|e),r[n]=W,f(n="POW_PRECISION")&&H(e,0,E,2,n)&&(J=0|e),r[n]=J,f(n="FORMAT")&&("object"==typeof e?X=e:j&&L(2,n+" not an object",e)),r[n]=X,r},a.max=function(){return x(arguments,T.lt)},a.min=function(){return x(arguments,T.gt)},a.random=function(){var e=9007199254740992,n=Math.random()*e&2097151?function(){return m(Math.random()*e)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)};return function(e){var t,r,i,o,u,s=0,f=[],l=new a(q);if(e=null!=e&&H(e,0,E,14)?0|e:P,o=d(e/y),V)if(h&&h.getRandomValues){for(t=h.getRandomValues(new Uint32Array(o*=2));o>s;)u=131072*t[s]+(t[s+1]>>>11),u>=9e15?(r=h.getRandomValues(new Uint32Array(2)),t[s]=r[0],t[s+1]=r[1]):(f.push(u%1e14),s+=2);s=o/2}else if(h&&h.randomBytes){for(t=h.randomBytes(o*=7);o>s;)u=281474976710656*(31&t[s])+1099511627776*t[s+1]+4294967296*t[s+2]+16777216*t[s+3]+(t[s+4]<<16)+(t[s+5]<<8)+t[s+6],u>=9e15?h.randomBytes(7).copy(t,s):(f.push(u%1e14),s+=7);s=o/7}else j&&L(14,"crypto unavailable",h);if(!s)for(;o>s;)u=n(),9e15>u&&(f[s++]=u%1e14);for(o=f[--s],e%=y,o&&e&&(u=R[y-e],f[s]=m(o/u)*u);0===f[s];f.pop(),s--);if(0>s)f=[i=0];else{for(i=-1;0===f[0];f.shift(),i-=y);for(s=1,u=f[0];u>=10;u/=10,s++);y>s&&(i-=y-s)}return l.e=i,l.c=f,l}}(),C=function(){function e(e,n,t){var r,i,o,u,s=0,f=e.length,l=n%A,c=n/A|0;for(e=e.slice();f--;)o=e[f]%A,u=e[f]/A|0,r=c*o+u*l,i=l*o+r%A*A+s,s=(i/t|0)+(r/A|0)+c*u,e[f]=i%t;return s&&e.unshift(s),e}function n(e,n,t,r){var i,o;if(t!=r)o=t>r?1:-1;else for(i=o=0;t>i;i++)if(e[i]!=n[i]){o=e[i]>n[i]?1:-1;break}return o}function r(e,n,t,r){for(var i=0;t--;)e[t]-=i,i=e[t]1;e.shift());}return function(i,o,u,s,f){var l,c,h,g,p,d,w,v,b,O,S,R,A,E,D,_,x,F=i.s==o.s?1:-1,I=i.c,L=o.c;if(!(I&&I[0]&&L&&L[0]))return new a(i.s&&o.s&&(I?!L||I[0]!=L[0]:L)?I&&0==I[0]||!L?0*F:F/0:0/0);for(v=new a(F),b=v.c=[],c=i.e-o.e,F=u+c+1,f||(f=N,c=t(i.e/y)-t(o.e/y),F=F/y|0),h=0;L[h]==(I[h]||0);h++);if(L[h]>(I[h]||0)&&c--,0>F)b.push(1),g=!0;else{for(E=I.length,_=L.length,h=0,F+=2,p=m(f/(L[0]+1)),p>1&&(L=e(L,p,f),I=e(I,p,f),_=L.length,E=I.length),A=_,O=I.slice(0,_),S=O.length;_>S;O[S++]=0);x=L.slice(),x.unshift(0),D=L[0],L[1]>=f/2&&D++;do p=0,l=n(L,O,_,S),0>l?(R=O[0],_!=S&&(R=R*f+(O[1]||0)),p=m(R/D),p>1?(p>=f&&(p=f-1),d=e(L,p,f),w=d.length,S=O.length,l=n(d,O,w,S),1==l&&(p--,r(d,w>_?x:L,w,f))):(0==p&&(l=p=1),d=L.slice()),w=d.length,S>w&&d.unshift(0),r(O,d,S,f),-1==l&&(S=O.length,l=n(L,O,_,S),1>l&&(p++,r(O,S>_?x:L,S,f))),S=O.length):0===l&&(p++,O=[0]),b[h++]=p,l&&O[0]?O[S++]=I[A]||0:(O=[I[A]],S=1);while((A++=10;F/=10,h++);U(v,u+(v.e=h+c*y-1)+1,s,g)}else v.e=c,v.r=+g;return v}}(),g=function(){var e=/^(-?)0([xbo])(\w[\w.]*$)/i,n=/^([^.]+)\.$/,t=/^\.([^.]+)$/,r=/^-?(Infinity|NaN)$/,i=/^\s*\+([\w.])|^\s+|\s+$/g;return function(o,u,s,f){var l,c=s?u:u.replace(i,"$1");if(r.test(c))o.s=isNaN(c)?null:0>c?-1:1;else{if(!s&&(c=c.replace(e,function(e,n,t){return l="x"==(t=t.toLowerCase())?16:"b"==t?2:8,f&&f!=l?e:n}),f&&(l=f,c=c.replace(n,"$1").replace(t,"0.$1")),u!=c))return new a(c,l);j&&L(M,"not a"+(f?" base "+f:"")+" number",u),o.s=null}o.c=o.e=null,M=0}}(),T.absoluteValue=T.abs=function(){var e=new a(this);return e.s<0&&(e.s=1),e},T.ceil=function(){return U(new a(this),this.e+1,2)},T.comparedTo=T.cmp=function(e,n){return M=1,i(this,new a(e,n))},T.decimalPlaces=T.dp=function(){var e,n,r=this.c;if(!r)return null;if(e=((n=r.length-1)-t(this.e/y))*y,n=r[n])for(;n%10==0;n/=10,e--);return 0>e&&(e=0),e},T.dividedBy=T.div=function(e,n){return M=3,C(this,new a(e,n),P,k)},T.dividedToIntegerBy=T.divToInt=function(e,n){return M=4,C(this,new a(e,n),0,1)},T.equals=T.eq=function(e,n){return M=5,0===i(this,new a(e,n))},T.floor=function(){return U(new a(this),this.e+1,3)},T.greaterThan=T.gt=function(e,n){return M=6,i(this,new a(e,n))>0},T.greaterThanOrEqualTo=T.gte=function(e,n){return M=7,1===(n=i(this,new a(e,n)))||0===n},T.isFinite=function(){return!!this.c},T.isInteger=T.isInt=function(){return!!this.c&&t(this.e/y)>this.c.length-2},T.isNaN=function(){return!this.s},T.isNegative=T.isNeg=function(){return this.s<0},T.isZero=function(){return!!this.c&&0==this.c[0]},T.lessThan=T.lt=function(e,n){return M=8,i(this,new a(e,n))<0},T.lessThanOrEqualTo=T.lte=function(e,n){return M=9,-1===(n=i(this,new a(e,n)))||0===n},T.minus=T.sub=function(e,n){var r,i,o,u,s=this,f=s.s;if(M=10,e=new a(e,n),n=e.s,!f||!n)return new a(0/0);if(f!=n)return e.s=-n,s.plus(e);var l=s.e/y,c=e.e/y,h=s.c,g=e.c;if(!l||!c){if(!h||!g)return h?(e.s=-n,e):new a(g?s:0/0);if(!h[0]||!g[0])return g[0]?(e.s=-n,e):new a(h[0]?s:3==k?-0:0)}if(l=t(l),c=t(c),h=h.slice(),f=l-c){for((u=0>f)?(f=-f,o=h):(c=l,o=g),o.reverse(),n=f;n--;o.push(0));o.reverse()}else for(i=(u=(f=h.length)<(n=g.length))?f:n,f=n=0;i>n;n++)if(h[n]!=g[n]){u=h[n]0)for(;n--;h[r++]=0);for(n=N-1;i>f;){if(h[--i]0?(s=u,r=l):(o=-o,r=f),r.reverse();o--;r.push(0));r.reverse()}for(o=f.length,n=l.length,0>o-n&&(r=l,l=f,f=r,n=o),o=0;n;)o=(f[--n]=f[n]+l[n]+o)/N|0,f[n]%=N;return o&&(f.unshift(o),++s),I(e,f,s)},T.precision=T.sd=function(e){var n,t,r=this,i=r.c;if(null!=e&&e!==!!e&&1!==e&&0!==e&&(j&&L(13,"argument"+w,e),e!=!!e&&(e=null)),!i)return null;if(t=i.length-1,n=t*y+1,t=i[t]){for(;t%10==0;t/=10,n--);for(t=i[0];t>=10;t/=10,n++);}return e&&r.e+1>n&&(n=r.e+1),n},T.round=function(e,n){var t=new a(this);return(null==e||H(e,0,E,15))&&U(t,~~e+this.e+1,null!=n&&H(n,0,8,15,v)?0|n:k),t},T.shift=function(e){var n=this;return H(e,-S,S,16,"argument")?n.times("1e"+c(e)):new a(n.c&&n.c[0]&&(-S>e||e>S)?n.s*(0>e?0:1/0):n)},T.squareRoot=T.sqrt=function(){var e,n,i,o,u,s=this,f=s.c,l=s.s,c=s.e,h=P+4,g=new a("0.5");if(1!==l||!f||!f[0])return new a(!l||0>l&&(!f||f[0])?0/0:f?s:1/0);if(l=Math.sqrt(+s),0==l||l==1/0?(n=r(f),(n.length+c)%2==0&&(n+="0"),l=Math.sqrt(n),c=t((c+1)/2)-(0>c||c%2),l==1/0?n="1e"+c:(n=l.toExponential(),n=n.slice(0,n.indexOf("e")+1)+c),i=new a(n)):i=new a(l+""),i.c[0])for(c=i.e,l=c+h,3>l&&(l=0);;)if(u=i,i=g.times(u.plus(C(s,u,h,1))),r(u.c).slice(0,l)===(n=r(i.c)).slice(0,l)){if(i.el&&(m=O,O=S,S=m,o=l,l=g,g=o),o=l+g,m=[];o--;m.push(0));for(w=N,v=A,o=g;--o>=0;){for(r=0,p=S[o]%v,d=S[o]/v|0,s=l,u=o+s;u>o;)c=O[--s]%v,h=O[s]/v|0,f=d*c+h*p,c=p*c+f%v*v+m[u]+r,r=(c/w|0)+(f/v|0)+d*h,m[u--]=c%w;m[u]=r}return r?++i:m.shift(),I(e,m,i)},T.toDigits=function(e,n){var t=new a(this);return e=null!=e&&H(e,1,E,18,"precision")?0|e:null,n=null!=n&&H(n,0,8,18,v)?0|n:k,e?U(t,e,n):t},T.toExponential=function(e,n){return _(this,null!=e&&H(e,0,E,19)?~~e+1:null,n,19)},T.toFixed=function(e,n){return _(this,null!=e&&H(e,0,E,20)?~~e+this.e+1:null,n,20)},T.toFormat=function(e,n){var t=_(this,null!=e&&H(e,0,E,21)?~~e+this.e+1:null,n,21);if(this.c){var r,i=t.split("."),o=+X.groupSize,u=+X.secondaryGroupSize,s=X.groupSeparator,f=i[0],l=i[1],c=this.s<0,a=c?f.slice(1):f,h=a.length;if(u&&(r=o,o=u,u=r,h-=r),o>0&&h>0){for(r=h%o||o,f=a.substr(0,r);h>r;r+=o)f+=s+a.substr(r,o);u>0&&(f+=s+a.slice(r)),c&&(f="-"+f)}t=l?f+X.decimalSeparator+((u=+X.fractionGroupSize)?l.replace(new RegExp("\\d{"+u+"}\\B","g"),"$&"+X.fractionGroupSeparator):l):f}return t},T.toFraction=function(e){var n,t,i,o,u,s,f,l,c,h=j,g=this,p=g.c,d=new a(q),m=t=new a(q),w=f=new a(q);if(null!=e&&(j=!1,s=new a(e),j=h,(!(h=s.isInt())||s.lt(q))&&(j&&L(22,"max denominator "+(h?"out of range":"not an integer"),e),e=!h&&s.c&&U(s,s.e+1,1).gte(q)?s:null)),!p)return g.toString();for(c=r(p),o=d.e=c.length-g.e-1,d.c[0]=R[(u=o%y)<0?y+u:u],e=!e||s.cmp(d)>0?o>0?d:m:s,u=z,z=1/0,s=new a(c),f.c[0]=0;l=C(s,d,0,1),i=t.plus(l.times(w)),1!=i.cmp(e);)t=w,w=i,m=f.plus(l.times(i=m)),f=i,d=s.minus(l.times(i=d)),s=i;return i=C(e.minus(t),w,0,1),f=f.plus(i.times(m)),t=t.plus(i.times(w)),f.s=m.s=g.s,o*=2,n=C(m,w,o,k).minus(g).abs().cmp(C(f,t,o,k).minus(g).abs())<1?[m.toString(),w.toString()]:[f.toString(),t.toString()],z=u,n},T.toNumber=function(){var e=this;return+e||(e.s?0*e.s:0/0)},T.toPower=T.pow=function(e){var n,t,r=m(0>e?-e:+e),i=this;if(!H(e,-S,S,23,"exponent")&&(!isFinite(e)||r>S&&(e/=0)||parseFloat(e)!=e&&!(e=0/0)))return new a(Math.pow(+i,e));for(n=J?d(J/y+2):0,t=new a(q);;){if(r%2){if(t=t.times(i),!t.c)break;n&&t.c.length>n&&(t.c.length=n)}if(r=m(r/2),!r)break;i=i.times(i),n&&i.c&&i.c.length>n&&(i.c.length=n)}return 0>e&&(t=q.div(t)),n?U(t,J,k):t},T.toPrecision=function(e,n){return _(this,null!=e&&H(e,1,E,24,"precision")?0|e:null,n,24)},T.toString=function(e){var n,t=this,i=t.s,o=t.e;return null===o?i?(n="Infinity",0>i&&(n="-"+n)):n="NaN":(n=r(t.c),n=null!=e&&H(e,2,64,25,"base")?D(l(n,o),0|e,10,i):B>=o||o>=$?f(n,o):l(n,o),0>i&&t.c[0]&&(n="-"+n)),n},T.truncated=T.trunc=function(){return U(new a(this),this.e+1,1)},T.valueOf=T.toJSON=function(){return this.toString()},null!=e&&a.config(e),a}function t(e){var n=0|e;return e>0||e===n?n:n-1}function r(e){for(var n,t,r=1,i=e.length,o=e[0]+"";i>r;){for(n=e[r++]+"",t=y-n.length;t--;n="0"+n);o+=n}for(i=o.length;48===o.charCodeAt(--i););return o.slice(0,i+1||1)}function i(e,n){var t,r,i=e.c,o=n.c,u=e.s,s=n.s,f=e.e,l=n.e;if(!u||!s)return null;if(t=i&&!i[0],r=o&&!o[0],t||r)return t?r?0:-s:u;if(u!=s)return u;if(t=0>u,r=f==l,!i||!o)return r?0:!i^t?1:-1;if(!r)return f>l^t?1:-1;for(s=(f=i.length)<(l=o.length)?f:l,u=0;s>u;u++)if(i[u]!=o[u])return i[u]>o[u]^t?1:-1;return f==l?0:f>l^t?1:-1}function o(e,n,t){return(e=c(e))>=n&&t>=e}function u(e){return"[object Array]"==Object.prototype.toString.call(e)}function s(e,n,t){for(var r,i,o=[0],u=0,s=e.length;s>u;){for(i=o.length;i--;o[i]*=n);for(o[r=0]+=O.indexOf(e.charAt(u++));rt-1&&(null==o[r+1]&&(o[r+1]=0),o[r+1]+=o[r]/t|0,o[r]%=t)}return o.reverse()}function f(e,n){return(e.length>1?e.charAt(0)+"."+e.slice(1):e)+(0>n?"e":"e+")+n}function l(e,n){var t,r;if(0>n){for(r="0.";++n;r+="0");e=r+e}else if(t=e.length,++n>t){for(r="0",n-=t;--n;r+="0");e+=r}else t>n&&(e=e.slice(0,n)+"."+e.slice(n));return e}function c(e){return e=parseFloat(e),0>e?d(e):m(e)}var a,h,g,p=/^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,d=Math.ceil,m=Math.floor,w=" not a boolean or binary digit",v="rounding mode",b="number type has more than 15 significant digits",O="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_",N=1e14,y=14,S=9007199254740991,R=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],A=1e7,E=1e9;if(a=n(),"function"==typeof define&&define.amd)define(function(){return a});else if("undefined"!=typeof module&&module.exports){if(module.exports=a,!h)try{h=require("crypto")}catch(D){}}else e.BigNumber=a}(this);
-//# sourceMappingURL=doc/bignumber.js.map
+
diff --git a/internal/jsre/deps/bindata.go b/internal/jsre/deps/bindata.go
index 97e167eb68..6f079c2ba5 100644
--- a/internal/jsre/deps/bindata.go
+++ b/internal/jsre/deps/bindata.go
@@ -1,7 +1,7 @@
// Code generated by go-bindata. DO NOT EDIT.
// sources:
-// bignumber.js (17.314kB)
-// web3.js (401.802kB)
+// bignumber.js (17.273kB)
+// web3.js (401.764kB)
package deps
@@ -21,7 +21,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: %v", name, err)
+ return nil, fmt.Errorf("read %q: %w", name, err)
}
var buf bytes.Buffer
@@ -29,7 +29,7 @@ func bindataRead(data []byte, name string) ([]byte, error) {
clErr := gz.Close()
if err != nil {
- return nil, fmt.Errorf("read %q: %v", name, err)
+ return nil, fmt.Errorf("read %q: %w", name, err)
}
if clErr != nil {
return nil, err
@@ -70,7 +70,7 @@ func (fi bindataFileInfo) Sys() interface{} {
return nil
}
-var _bignumberJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\xbc\x6b\x77\x9b\xc8\x93\x38\xfc\x7e\x3f\x85\xc4\xc6\x9c\x6e\x53\x20\x90\x9d\x38\x86\x14\x9c\x4c\x62\xe7\xe7\x79\x1c\x3b\x4f\x9c\xcc\xcc\xae\xa2\xc9\x91\x51\x23\x75\x82\x40\xe1\x62\xc7\x09\xfe\x7d\xf6\xff\xa9\x6e\x40\xf2\x25\xbb\xb3\x6f\x2c\xe8\x4b\x75\x75\x75\xdd\xbb\xf0\x68\x77\x70\x29\x17\x59\xbd\xba\x14\x85\xf3\xa5\x1c\x5c\x8d\x1d\xd7\xd9\x1b\x2c\xab\x6a\x5d\xfa\xa3\xd1\x42\x56\xcb\xfa\xd2\x89\xf3\xd5\xe8\xad\xfc\x2a\xde\xc6\xe9\x68\x7b\xf8\xe8\xf4\xe4\xd5\xd1\xd9\xab\xa3\xc1\xee\xe8\x3f\x46\xbb\x83\x55\x3e\x97\x89\x14\xf3\xc1\xe5\xcd\xe0\x87\x48\xe5\x62\x50\xe5\x83\x44\x7e\x7f\x0c\x5c\x91\x5f\x8a\xa2\xfa\x5a\xc8\x95\xc8\x46\x79\x55\xe5\xff\x59\x88\x45\x9d\xce\x0a\x5b\x7c\x5f\x17\xa2\x2c\x65\x9e\xd9\x32\x8b\xf3\xd5\x7a\x56\xc9\x4b\x99\xca\xea\x86\x96\x19\x26\x75\x16\x57\x32\xcf\x98\xe0\x3f\x8d\xba\x14\x83\xb2\x2a\x64\x5c\x19\x41\xd7\x31\x50\x5d\xfd\xdb\x8c\x09\xc8\xf8\xcf\xab\x59\x31\xa8\xa0\x00\x09\x39\xd4\x50\x42\x82\xd5\x52\x96\x81\x4c\xd8\x90\x25\x03\x99\x95\xd5\x2c\x8b\x45\x9e\x0c\x66\x9c\x17\xa2\xaa\x8b\x6c\xf0\xc5\x34\x4f\xd9\xf8\x19\x18\x71\x9e\x95\x55\x51\xc7\x55\x5e\x0c\xe2\x59\x9a\x0e\xae\x65\xb5\xcc\xeb\x6a\x90\x89\x6b\x03\x04\x87\x4c\x5c\xb7\xeb\x10\xc0\xac\x4e\xd3\x21\x66\xa6\xf9\x2f\x96\xc1\x18\x9e\xed\xc3\x5b\x30\x2e\x67\xa5\x30\x38\xff\x49\xfd\xe8\x36\x19\x94\x28\x2c\xc3\x00\xcf\x45\xcc\xba\x15\x13\x6c\x21\xdd\x41\x28\x12\x7e\xc9\xe1\x23\x4b\xe0\x9d\x95\x38\xc2\xf2\xe0\xab\x5a\x87\xe5\x68\xe8\xa3\x30\x10\xab\x9b\x35\x0d\x16\xdc\x34\xdd\x5d\x31\x44\xb7\x69\x86\x04\xec\xbd\x58\x1c\x7d\x5f\x33\xe3\x6f\x3b\x32\x2c\x56\xa1\x31\x31\xac\x73\xa7\x4c\x65\x2c\x98\x0b\x19\xb7\x8c\xa9\x65\x70\xcb\x60\x91\xff\xe9\x93\x63\x58\x95\x65\xf0\xe8\x89\x01\x7b\x07\x61\x16\x19\xd2\xf0\x0d\x83\x3b\x95\x28\x2b\x56\xf6\x84\x59\xb0\x04\x4a\xc8\x69\xbb\x79\xc4\x12\xa7\x44\x37\xf4\x46\x22\x62\x25\x96\x2d\x68\x8f\x83\xed\x71\xdf\x83\x2f\xa6\x59\x3a\x85\x58\xa7\xb3\x58\xb0\xd1\xdf\xee\x27\xc7\xdd\x6d\x3e\x39\x23\x20\xb8\xa9\xc8\x16\xd5\x32\xf4\x9e\x12\xa5\xdf\xc2\x25\xd1\x32\xc7\xa1\xc7\x7d\x02\xba\xff\x14\x11\x4b\x27\x5e\xce\x8a\x57\xf9\x5c\xbc\xac\x98\xcb\x1f\x5d\xa3\xc4\xd7\xac\x04\xcf\x85\x0c\x12\xa7\xe4\xb7\x22\x2d\x05\x11\xfa\x2e\x19\x7b\x22\x3b\x25\x0a\xa7\x84\xc4\x11\x28\x1c\x01\x89\x13\x23\xa3\xc7\x98\x47\xa2\x05\xcd\x7d\x01\x57\xb9\x9c\xb3\xb7\xe8\xfe\x6f\xb4\x46\x74\xd5\xb1\x6e\xd1\x41\xa0\x2d\x5a\xdc\x04\x22\xfe\xfb\xdf\xc4\x90\x79\xc1\x0a\x74\x41\xa2\x08\x64\x88\x9e\x1b\xc8\x11\x7a\x2e\x14\x96\xc5\x83\x1e\x35\x81\x85\x42\x68\x22\xa6\x1b\x04\x6e\x35\xaf\xf4\xfb\x1a\xae\xdb\x13\x51\xcd\xf7\x8f\x85\x07\xff\x17\xe2\xdd\xde\x12\x62\xac\xc0\xd2\x91\xd9\x5c\x7c\x3f\x4f\x98\xe1\x18\x9c\x87\xb6\x67\x9a\x6a\x7c\x77\x78\x86\x63\xd0\xa1\x71\x60\x92\xa0\x88\x59\x11\x2f\xd9\x48\x8c\x24\xe7\xa1\x1b\x31\x37\x2c\x4c\x93\x15\x28\x39\x14\x16\x5a\xdd\x3a\xd2\xf2\x38\xa8\x65\xeb\x4b\x92\xd4\x6c\xc1\x5c\x90\x9c\xfb\xdd\xf8\xb2\xe5\x02\x0e\x12\xdd\x60\xff\xf9\x7d\xb4\x25\x0f\x24\x91\x88\xd0\xac\xfb\xd1\x8f\x0c\xb4\xed\x9a\x07\xea\xb0\x36\xbb\x94\x50\x5b\x1e\xe7\x32\xd9\x9a\x0a\xb9\x69\x7e\x31\xcd\x7a\x8b\xed\x12\xa7\xdc\x15\x1c\x0a\x2c\x6c\x69\x7b\x50\x84\x3f\x38\x1d\x02\x1d\x07\x09\x73\x40\x84\x1f\xc8\x84\xbd\x09\x0b\xd5\x31\xa1\x1e\x77\x1a\x74\x07\xb2\x75\x6e\x53\x90\xc8\x0a\xcb\xe3\x3b\x37\xa0\xb7\x28\x2d\xbc\xe1\x50\x87\x52\xf3\x80\x34\xcd\xc4\x89\x9d\x75\x5d\x2e\x59\x4f\x25\x45\x12\xa8\x6d\xbc\x09\xea\x50\x06\xfc\xe1\x08\x09\x0a\x0e\x0f\xb6\x36\x47\x24\xbb\xb1\xbb\x7d\xdd\x6a\x2c\x6d\xac\x15\xad\x02\x69\xdb\x41\x69\xa1\xe1\x1a\xc4\x11\x3d\x3c\x2d\x1e\x83\xed\x6d\xbc\x45\xf7\xb6\xd7\x97\xaf\x49\x8f\x41\x05\x52\xeb\x4c\xd2\x96\x09\xc4\xb0\x84\x05\xac\x61\x8e\xe2\x0e\x9b\xc0\x0a\xdf\xc1\x35\x7e\x55\x2b\xee\x1d\x84\x95\x69\x2a\x51\xaa\xf2\xd3\xfc\x5a\x14\xaf\x66\xa5\x60\x9c\xc3\x3c\x44\xd7\x34\x59\x82\xbf\xc3\xef\xe8\x02\x8d\xb8\xc7\x55\xb0\x6e\x55\x5f\xc5\x61\x89\x6b\x67\x9d\x5f\x33\xd1\x6e\xcc\x9e\x73\xf8\x1d\x13\x58\x3b\x31\x96\x2c\x65\x05\x5b\x3a\x31\x87\xa5\x23\xb8\x12\x7a\x0e\x6b\x47\xe0\xda\x89\x7b\x4e\x5a\x60\xc9\x04\x54\xd4\x55\x63\x82\x8b\x8e\x69\x5c\xc4\xc5\xc4\xb6\x93\x69\xb0\x70\xd6\xf9\x9a\x71\xc5\x2e\xc3\xc5\xc4\x9d\xb6\x42\x64\xb8\x06\x35\xb9\xe1\x3c\xb2\xed\xda\xa7\x95\x70\x41\x4b\x61\x0d\x4b\xa7\x44\x09\x4b\x7c\xc5\x96\xb0\x86\x15\x5c\x13\xfc\x05\x2e\x9d\x18\x62\x5c\x3a\x05\xd4\xa8\x70\xca\xb1\xb6\x56\x96\x07\x73\x5c\x4c\xf2\x29\x24\x98\x8d\xc6\x10\x63\xdc\x34\x6e\x98\x37\x8d\x36\x0f\x8b\x49\x6e\x79\x53\x88\x71\x3f\xbc\x8e\x5a\x93\x31\x6f\x9a\x98\x9b\x26\x73\x11\xaf\x9b\xe6\x1a\x91\x2d\x9d\xf2\x85\x1b\xed\xf9\x63\xce\xfd\x79\x98\x34\xcd\x1c\x31\x31\x4d\xb6\xaf\x46\xc4\x4d\xf3\x0c\xf1\xda\x34\x3d\x73\x31\xc9\x6d\x6f\xba\x3d\xe9\xb9\x7f\xc0\x39\x78\xb4\xa2\xde\xa0\xc0\x38\x4a\x99\xe1\x19\x60\xaf\xb8\x4f\x1b\xed\xd8\xb7\xa3\x0f\xe6\x10\x73\x3a\x49\xdb\xce\x02\xcb\x22\x52\xe5\xd3\x30\x0b\x38\xed\x03\x5d\xc8\x9b\x86\x59\x56\x0d\x0b\xa7\xce\xca\xa5\x4c\x2a\xe6\x71\x2d\x98\x5b\x34\x1e\xb6\x14\xd6\x1d\x73\x75\xdc\x86\x11\x24\x21\xce\x03\x61\xe1\xb9\x12\xd9\x97\x15\x5b\x4c\xe6\x96\x35\xe5\x3c\x10\x98\x32\x01\x35\xbf\x6d\xd5\x98\xd8\xf0\xe2\xe7\x87\xbc\x58\x12\x2f\xd2\x11\x55\xa8\x89\x56\x91\x9d\xad\xc0\x85\xe7\x20\xe1\x8a\x47\x6e\x53\xf9\x5f\x61\x48\xea\xbc\x03\xe8\x54\xf9\x85\x56\x3d\xea\xbc\x73\xd2\xf5\x13\x77\x4a\x26\xd8\x11\x40\x60\xc8\x06\x2f\xb1\x60\x42\x31\x16\x7a\x87\x88\xb2\x69\xc6\xfb\x88\xd2\x34\x7f\x0b\xb1\x8c\x12\xb6\x84\x92\xfb\xa9\xfa\xe9\x15\x82\xc0\x8f\xac\x35\xd9\x9c\x30\x25\x7e\x23\x98\x3d\x2c\x62\x8c\x56\xed\xdc\x05\xca\xea\x10\xb3\xa6\xf9\x2d\xc4\x9a\x6b\xc5\x10\x64\x61\x1c\x2c\x95\xc0\x42\x4c\x1a\x6f\x89\xb4\x68\xdd\x0a\x2c\x39\x0e\x36\x96\xb0\xc4\x54\xb5\x92\x66\x0b\x63\x65\x79\x6c\x3b\x0b\x5d\x75\x70\x34\xdd\x31\x82\xcc\xb6\x5b\x48\x3c\xd8\xcc\xb6\xb0\xb6\x63\xe8\x86\xd6\x96\x87\x18\x9b\x66\x3b\x87\xdf\x99\xd4\x53\xae\x7c\xe1\x9a\x66\x1e\x19\xb6\x61\x2d\xfd\xe5\xe6\x64\xbe\xdf\xf3\xaa\xd0\xd5\x0a\x9a\x09\x62\x35\xad\x05\xe8\x09\xaa\xce\xa5\xa1\xb7\xc0\xb2\xe4\x8b\x4e\xac\x03\x85\x7b\xd1\xf7\xcb\x29\x87\x61\xe1\x94\xfc\x67\x85\x45\x70\x59\x88\xd9\xd7\xdb\xcc\x21\x7f\x8b\x55\x50\x10\xcc\x0a\x8b\x9e\x4b\xaa\x0d\x2e\xc7\x2d\x97\x14\xc4\x27\xba\x9b\x65\xa1\x68\x1a\x11\x56\x4d\x23\x86\x18\x33\xc1\x39\xe9\xfa\x02\x98\x6c\x1a\x63\x2e\x62\xb9\x9a\xa5\x03\xa5\x81\x4a\x83\x5b\xfd\xf0\xc8\x18\x90\x5f\x97\x27\x83\x62\x96\x2d\x84\xe1\x1b\x83\x2c\xaf\x06\xb3\x6c\x20\xb3\x4a\x2c\x44\x61\x70\xf2\x51\x86\x5b\xfa\xf2\x44\xaf\xae\xcf\x90\xe8\x51\xa0\x07\x12\xb3\x5e\x1e\xb2\x89\x6d\xcb\x69\x90\x75\x1a\x47\x19\x01\xcc\x26\xee\xf4\x57\x7e\x00\x6d\xd4\xaa\x76\x6f\x6c\x8f\x87\x3f\x22\xe1\xc4\xc4\x53\x8a\xdd\xfd\x37\x61\xa5\x1a\x26\x42\xa9\x6e\x9f\xd1\x6f\x05\xd4\x94\x71\xd8\x12\x9d\xd3\x0e\x2d\x8d\x12\x11\xf9\xa8\x28\xf2\x82\x4d\x0c\x7a\xfe\x4d\x2e\xce\xb4\x3b\x03\x46\xbc\x5a\x1b\xca\xc9\x4d\xe4\xc2\x00\x63\x2e\xaf\xf4\xdf\x0f\xf9\x49\x56\x19\x60\x88\x6f\x06\x18\x8b\x4a\xfd\x11\x06\x18\x69\xa5\xfe\xd0\xe3\x4a\x66\x75\x49\xbf\xf9\xdc\x00\x63\x9d\xaa\x97\x75\x21\x62\x49\xfe\xbb\x01\x46\x31\xcb\xe6\xf9\x8a\x1e\xf2\x3a\xa3\x31\x4a\x6f\x18\x60\x54\x72\x25\x68\x70\x95\xbf\x96\x0b\x59\xe9\xc7\xa3\xef\xeb\x3c\x13\x59\x25\x67\xa9\x7a\x3f\x96\xdf\xc5\x5c\x3f\xe5\xc5\x6a\x56\xe9\xc7\x62\xa6\xb6\x48\x2b\xe5\xd7\xaa\xe9\xdd\xd6\x8a\x9d\xac\x1b\x60\x6c\x36\x39\x9d\x88\xa9\x65\x30\x3e\x30\xac\xcc\x32\xfc\x81\x61\x55\x3c\xa8\x96\x45\x7e\x3d\x28\x9c\x6c\xb6\x12\xb8\x19\xac\xe9\x64\xc0\x5b\x74\xa1\xd8\x10\xf4\x63\xc7\x65\x9a\xa4\x7d\x1c\x01\x29\xc4\x30\x23\x95\x02\x4b\x7c\x4f\xfa\x65\xc6\x7f\x0a\x5f\xdb\x7a\x24\xe7\x74\x46\x47\x5d\xaa\xa3\x2e\xd5\x51\x2b\x7f\x46\x29\xa2\xcc\x96\xe0\x86\x39\xcf\x2d\xbc\x81\x1a\x33\x48\x70\x36\x49\xd1\x25\xc3\x90\x8c\x96\x13\x69\xd7\xb6\x37\xdd\xf1\xdc\xc6\xed\x75\x4e\x8a\x73\xc6\x72\xcb\xe3\xa3\x1b\x0e\x69\x88\xb3\xce\xec\x29\xd7\xb0\xe0\x4a\x72\x06\x42\x3b\x01\x5d\xe7\x0b\x4c\x83\x99\x76\x01\x5c\xe2\x41\x8c\x95\x2b\xea\x41\xbe\xa3\x56\xce\xed\x1b\xcb\xd3\x0e\xa6\xd6\xe7\x84\x76\x4a\xce\x8c\xf7\x10\xf5\xad\x39\x12\x62\x74\xc3\x3a\x72\xfd\x7b\xe8\xde\x2a\xd9\x2e\xc8\xe6\x65\x9d\xcd\x9b\x4d\x52\x8b\x8c\x14\xa3\x19\x89\x9f\xec\x74\x33\xc8\xf5\xda\x0f\xab\x88\xc5\x4d\x53\xb4\x16\xb0\x6a\x9a\x0a\x91\x89\x2d\x0b\x18\x87\x4f\x9b\xe6\xa9\xd6\x5a\xfb\x6a\x44\xa1\x2c\x20\x79\x1d\x79\xe8\x46\x75\xe8\x46\x2d\x1a\x53\xdf\xf5\x67\x93\x94\x60\xef\x78\xae\xe9\x6d\x03\xeb\x2c\x63\xd6\x34\xc3\xd9\xc6\xf4\x0f\x3a\x5a\xd1\xb9\x47\xa4\x6c\x85\x0a\xb6\x68\x08\x2e\x27\xd9\xce\xcd\x14\x48\xda\xec\xac\x69\x5c\xee\xab\x66\x25\x85\x20\x94\xcb\x80\x98\x47\xac\x87\x91\x42\x89\x1e\xa4\xb6\xcd\xfd\xad\x46\x8b\xf8\x61\x39\xb9\xb1\xf3\x29\x10\x7d\x91\x50\x5e\xb1\x0e\xe9\x9d\xe5\xa4\x9e\xf2\xdd\xd2\x77\x39\x14\x4a\x4b\x07\x5a\x4b\xba\x88\xa9\xd6\x30\x39\x7a\x50\x6b\x96\xaa\xd5\xb9\xd4\xea\x5c\xf2\x8d\x8b\x4c\x7d\x16\x96\xb4\xfe\x9d\x21\xa5\x3a\xba\x21\x96\xa4\x9d\x1d\x61\x59\x7a\x67\x78\x66\x9a\x4c\x3d\x91\x31\xd7\x6a\x97\x98\x78\x92\x2a\x28\xf4\x3b\xc4\x33\xcd\x55\x01\x91\xd4\x26\x57\xa0\x44\xef\x56\xa3\x33\xdb\x72\xae\x70\xa6\x5c\x06\xe2\x34\xad\xeb\x6e\x85\x23\xee\xab\x30\xe1\x88\x17\x6f\x14\x0e\xbd\x1a\xdb\xb2\xfd\x24\x5b\xaf\x94\xec\x7d\xc0\x99\xb3\x2e\xf2\x2a\xa7\x70\x0b\xbe\xb5\x76\xc2\xe3\xf0\x0e\xc7\x2e\x7c\xc5\x7d\xf8\x0d\xed\x03\x78\x82\x63\x0f\xde\xa0\xed\x89\x03\xf8\x81\xf4\xf7\x0b\x0e\x5d\xf8\x17\x1e\xc3\x1f\x38\xf4\xe0\x4f\xf4\xe0\x77\xf4\x5c\x17\xfe\xc2\x9f\xad\xe6\xbf\x10\xeb\x59\x31\xab\xf2\xc2\x27\xf7\x73\x51\xe4\xf5\x7a\xab\x09\xba\x26\xf9\x43\xf8\x7b\x50\x8a\x38\xcf\xe6\xb3\xe2\xe6\x4d\xdf\xe8\x42\xd2\x2a\xa1\x37\xf7\xe6\x0e\x8c\x7b\x5d\x6a\xf8\x6d\xd0\xb3\xd8\x2c\xcb\xab\xa5\x28\x30\x83\x99\xf3\xfe\xfc\xe3\xd9\xeb\xcf\x1f\xdf\xa1\xdb\xbf\xbc\x3e\xff\xf3\x0c\xbd\xfe\xf5\xd5\xd1\xc9\x29\x8e\xfb\xd7\xe3\xd3\xf3\xf3\xf7\xb8\xd7\xbf\xff\xeb\xe5\xe9\x31\xcd\xdf\xbf\xdb\xa2\x80\x3c\xbd\xdb\x76\xf4\xc7\xd1\x19\x3e\xbb\xdb\xa6\xa0\x1f\xdc\x6d\xd3\x4b\x3c\x87\x99\x73\xf4\xf1\xd5\xe9\xc9\x6b\x3c\x84\x99\xa3\x6d\x03\xf6\xa9\x17\xad\x02\x95\x3e\x24\x61\xc1\x9f\xb7\x20\x71\x56\x2c\xea\x95\xc8\x2a\xe2\x3c\x49\xee\x55\x42\xac\x66\xe4\x97\x5f\x44\x5c\x6d\xa2\xe6\x32\xda\x02\xd3\x92\xa5\x74\x96\xb3\xf2\xfc\x3a\x7b\x57\xe4\x6b\x51\x54\x37\x2c\xe3\x91\x56\x19\x4c\x60\x39\xc9\xa6\xdc\xa7\x60\x78\xe0\xde\xfa\x0f\x27\xcb\x2e\x8d\x50\x6d\xe6\xc8\x49\x45\xce\x65\x37\xab\x8f\xaf\x59\x86\xc6\xeb\xa3\x57\x27\x6f\x5f\x9e\x7e\x7e\x77\xfa\xf2\xd5\xd1\x85\xc1\xc9\x7f\x14\xe0\xc2\x11\x8c\x21\x23\xe5\xf3\x0e\xdd\x86\xa2\xc1\x49\x36\xc5\x77\xa0\xe6\x28\x02\x9d\x9c\xbd\xf9\xfc\xf6\xfc\xf5\xd1\x66\xca\xf3\x6e\xca\xd7\xad\x29\x5f\xf5\x94\xa3\xbf\xde\x9d\x9f\x1d\x9d\x7d\x38\x79\x79\xfa\xf9\xe5\x07\x9a\x43\xde\x11\x8f\xfe\xa5\x5c\x21\xb0\x8f\xc0\x6d\x67\x53\x8b\x37\xdd\xc6\xe0\x37\x02\x47\xa3\x9e\xa8\x07\x6f\xca\x7d\x5a\xd0\x3e\xda\x1e\x62\x33\xea\x65\x6e\x28\x22\x5b\xf8\x82\x73\xde\x22\x30\xf9\x0d\x9e\x4c\x5b\xbc\x5f\x9e\xbd\x39\x7a\x6c\x6d\xdb\xbb\xbb\xb8\xb7\x81\xfc\xa6\x5b\xfc\xc7\x2f\x17\x77\x1b\x11\xbd\x41\x9b\xfd\xb8\x8b\x80\xaf\x33\x66\x90\x59\xc6\x20\x9e\x65\xe4\x39\x5d\x8a\xc1\x0f\x51\xe4\x06\x88\x0d\x7a\x6f\xe0\x47\x8b\xde\xd1\xfb\xf7\xe7\xef\xd5\x11\x30\x81\x88\xc3\xa1\x68\x1a\x0f\x11\x45\xd3\x90\x36\x11\x11\x23\x45\xf0\x2f\x64\x5f\xa8\x8f\x47\xc7\x7e\xbe\xb5\xc8\x35\x01\xd5\x30\xbf\x68\x78\xaf\xde\xff\xd7\xbb\x0f\xe7\xff\x13\xbc\x3f\x70\xc8\xa8\x75\xb8\x6c\x9a\x8e\x35\x87\x1d\x6b\x2e\x39\x08\xd3\x1c\xfe\xa1\xf2\x03\xb4\x86\x11\x17\x37\xeb\x2a\x1f\xd4\xd9\xec\x6a\x26\xd3\xd9\x65\x2a\x0c\x58\xf2\xc7\x71\xf8\x43\xe3\xf0\xf6\xfc\xf5\xc7\xd3\xf3\x7b\x8c\x72\xd8\x51\xee\xcf\x2d\x46\xf9\x53\x4f\x78\x77\xfe\xe7\xe7\x77\xef\x8f\x5e\x9d\x5c\x9c\x9c\x9f\x3d\xc2\x8e\xbf\x6f\x4d\xf9\x5d\x4f\x39\x3e\x7f\xff\xb6\xe5\xa9\x07\xf2\x25\xa2\xbf\x50\x6c\x9f\x44\xeb\xc0\xb6\xe3\x36\xf8\xfe\x05\xc5\x2d\xcc\x9c\xd5\xec\x3b\x3e\x14\xaa\xef\x6c\x23\xce\x1f\x9c\xb4\xe2\x6a\xa8\xcc\xfe\xd7\xa1\x0b\x3d\x54\xfb\x7d\x0f\x34\x06\x1e\xba\xee\x81\x77\x78\x38\x7e\xba\x7f\xb0\xef\x1e\x1e\x8e\x21\xc3\xb7\xb3\x6a\xd9\x8e\x67\x7c\x57\x98\x63\xf7\xf0\xc0\x7b\xea\x3d\xa2\x26\x56\xec\xde\x58\xfe\x98\x3e\x78\xbe\xf7\xfc\xf9\x33\xf7\xf9\x2e\xf3\xdc\x83\xbd\x83\x7d\xef\xf9\x78\x7f\xf7\xce\xbc\xc6\xe5\x16\xeb\x46\xdd\xef\xd9\xe8\x8a\xad\x3c\xf3\xbd\xe4\x31\xba\x90\xe0\x64\x0a\x69\x6b\x93\xbe\x29\x6f\x4e\xb4\x01\xa9\xd8\x9c\xa0\xb7\x4f\xf1\xa8\xf0\xdf\x41\x8e\x73\x26\xc8\x61\xfb\x83\xcb\x84\x2d\x4d\x73\xe9\x2c\x44\xf5\x5e\xad\xfb\xc7\x2c\xad\x45\xa9\xcd\x7b\x85\x0f\x3a\x54\x80\xf9\x51\x66\xd5\xde\xf8\x65\x51\xcc\x6e\x58\xbe\x8b\x63\xce\x83\x3c\x2c\x03\x5e\xa3\xb7\xe7\xb9\x07\xe3\xdd\x6a\x52\x4e\x2d\x56\x4d\x4a\xcb\x9b\x86\x61\xe8\x79\x1c\xea\x10\x0f\x85\xf7\x34\x62\xc5\x3f\x00\x3a\xe6\x1c\x08\x06\x16\x24\xfa\x1a\x0e\x16\x4a\xfa\x59\xa2\x1d\xc7\x7a\xc7\x13\xde\x3e\x87\xd2\xc2\x31\x0f\x4a\xcc\x47\xe3\x3e\xb8\x54\x3b\xd2\x64\xfc\xed\xa6\xda\xde\xcd\x56\x23\x61\x7e\xd0\x23\x3e\x7e\xee\xed\x1f\xec\x1f\x1e\x3c\x3b\xf0\xdc\x67\x4f\x9f\xed\xb2\x3d\xcf\x24\x0c\xb8\xe5\xb9\x87\x87\x4f\x3d\xef\xd9\xf8\xe0\xe0\xe0\xd9\xae\xc6\xc5\xda\x1f\x1f\xee\x1f\x3e\x3b\x18\x1f\xea\x96\xf1\xd4\xf2\x9e\x1d\x1c\x1c\x8c\x3d\xfd\xbe\xd7\xee\x7e\x7f\xfa\xe2\x85\xf7\x8c\xeb\x97\xa7\xd3\x17\x2f\x9e\x73\x8b\x1e\x9f\x4d\x7b\x7a\xdc\xc5\xe9\x80\x3b\x71\xbe\xbe\x61\x15\x85\xf7\x8f\x6c\xf5\x40\x6f\xf5\x40\x6f\x55\xc9\x95\xb7\xff\x2b\xcd\xa0\xd2\x49\xa5\xf6\xdc\xda\x6d\x66\x8c\x03\x2d\x1b\xd6\xa6\xc9\x92\x49\x69\x59\x53\x6c\xc1\x07\xda\x83\x4a\x26\xb6\x5d\x4e\x41\x90\x57\x9d\x9b\xa6\x20\x6d\x8d\xef\x27\x37\xb6\x98\x42\x42\x47\xb2\x62\xf9\xa8\xe6\xbb\x35\x57\x3e\x16\x35\x05\x89\xf6\xb0\xa0\xb4\x6d\xae\x13\x56\x25\x4f\x70\x22\xfb\xac\xa4\x0e\x3f\x6c\xaf\x9d\xe2\xd2\x14\x9d\xb3\xe1\x20\x6d\xbc\xd1\x8b\x97\xca\x9b\x4c\xee\x7b\x93\xca\x55\xbc\x09\xc9\x53\xa4\xb1\x76\xd9\x3b\x68\xa9\x23\x50\x42\xea\xc4\x98\x40\x7a\x7b\xcb\x38\xbc\xda\x16\xf2\x3e\x5a\x12\x77\xc2\xcf\x3b\x82\xd3\xc5\xff\x24\x3e\x3b\x2f\x21\xc6\x6c\xf4\xb2\xd1\xe9\x03\x81\x7d\x02\x3e\x48\x6c\x3b\xe0\x39\x8a\x49\x32\xdd\x79\x09\xb5\x7a\xa0\x81\x50\x60\xbc\x9b\x5b\xf5\x6e\x0a\x12\xd3\xdd\xdc\x2a\x76\x5e\xee\xbe\xb4\xc8\xeb\x60\x72\x54\x29\xe1\x2e\x68\x20\xb7\xe2\xdd\x1a\x68\x1a\xca\x9d\xaa\x13\xeb\xd2\x34\x45\x9f\xbe\x2a\xef\x84\xcc\xd9\x83\x08\x4f\xe5\x99\x86\x58\xf0\x1c\xab\xb0\x88\x3c\xdf\xf6\x74\x18\xa6\xa9\x9b\xa3\x1b\x54\xa1\x54\xf9\x69\x52\x00\x13\x39\x1d\x62\x36\x91\x53\xfe\x93\x10\x97\xd3\x90\x5e\xf4\x34\xed\x58\xb7\x48\xe4\x9b\x45\x8b\xcd\xa2\x5d\x02\x41\x12\x58\xda\xbd\x98\x54\x53\x1b\x25\x48\xa4\xa7\x17\xd9\xa4\x22\x60\x2e\xd0\x1b\xca\xdd\xc2\x52\x03\xa8\x59\x07\x7b\x43\x32\xdb\xb4\xbf\xee\x5e\x25\x10\xdd\x99\xf3\xe0\xf6\xbe\x5e\xeb\x23\x58\xbd\xdd\x74\x93\xe4\x85\x6b\xb8\x82\x4b\x38\x87\x0b\x78\x0f\x2f\xe1\x08\x5e\xc3\x67\xf8\x0e\xc7\x28\x9d\x12\x31\x77\x4a\xb5\x25\x38\x41\xe9\xc4\x70\x8a\xb9\x13\xeb\x7b\xb4\x13\xd3\x3c\x51\x18\x9c\x9a\xe6\x29\x05\x56\x5d\x64\xa5\xd5\xa4\x74\x4a\xd3\xcc\xe9\x0f\x3b\x89\x86\xa7\x4d\x43\x83\x87\x48\x23\xfd\x53\x1e\x9d\x98\xa6\x8b\x48\x6d\x4d\x33\x3c\x8d\xdc\xdd\x63\xff\x78\xe4\xfa\xee\xc8\xd5\xbc\x7a\xd5\x6a\xdb\x63\x0e\x97\x78\xa5\x73\xed\x31\x4a\x47\xd8\xb9\x23\xe0\x18\x6b\x2b\xb6\x3c\x48\x9a\x86\x25\x78\x06\x31\x56\x4c\x3a\xa4\x72\xed\x8a\xe5\xea\x01\x8e\xf1\x78\x74\xd3\xb8\x1c\x96\xe8\x06\xa7\x93\xe5\x14\x91\x9d\x4c\x96\x53\x8a\xe7\x82\x65\x1b\x94\x53\x7b\xd8\x37\x9b\x66\x6c\xdb\xe0\x86\xc7\xfc\x52\x6b\x06\x8f\xc3\x02\x87\xee\x46\xc8\x8e\xf0\xa4\x63\xe8\xcf\x78\xda\x3d\x52\x10\x79\x6c\xe1\x18\xd6\x48\xe1\x1d\xa3\x4d\x5a\x1e\xe7\xb0\x0e\x3d\xd3\x64\xa7\x28\xd8\x29\xac\x21\xe1\x70\x82\x82\x9d\xe8\xc7\xad\xf9\x1b\xa8\x1c\x5e\xe2\x67\x38\xc7\x93\xfe\xaa\xe0\x33\x87\x0b\x3c\xef\xc2\xae\xcf\xe1\x45\x70\x3e\xb9\x20\xb5\xe2\xf2\xe0\x3b\x9e\x76\x12\x04\xdf\x7b\x3e\x77\x39\xbc\x56\x74\x86\xd3\x89\x37\x0d\x31\x19\x8d\x4d\xf3\xb5\x65\x05\xf3\x7c\xb0\x46\x97\x24\x91\x9d\xc2\x39\x7c\x86\x0b\x0e\x6e\x98\x46\xec\x3d\x9e\xd3\xf0\xcf\x43\xbc\x30\x4d\xf6\x1e\xdf\xef\x26\x16\x3b\x9f\x78\x8a\x28\x5c\xed\xea\xfd\xe8\xb5\xda\x4e\xc4\xd6\xa1\x4a\x4a\xaf\x31\xb1\x3d\x0e\xf3\xcd\xde\xae\x71\xde\x6d\x68\x83\xb1\x5a\x6d\x0e\xe7\x70\x4d\xab\x79\x88\x29\xcd\xb5\x6d\x28\xd8\x1c\xae\xc3\xcf\xd1\x77\xff\x14\xae\x21\xe1\x9c\xfb\x14\xf8\xae\x4d\x93\xa5\xb8\x46\x05\xba\xdf\xdd\x5d\xe0\xe1\xb5\x69\xce\xb7\xb7\x5b\xb0\x73\x98\xc3\x05\x21\x61\xb7\x4b\xdc\xc3\xa0\xdf\xaf\x17\x2a\x04\x2c\x4b\x4d\xba\x68\x11\xb8\x50\x08\x6c\xa1\xcd\x7d\xd2\xa4\xdd\xd0\x73\x54\xd9\xcd\xcb\xc9\x92\x08\xbf\x86\xd4\x34\x89\x60\x51\x7b\x12\x27\x93\x97\x44\x29\x9f\x9d\xe3\x84\x9e\xa7\x70\x81\x1e\x0f\xae\x97\x32\x15\x8c\xbd\xb4\xac\x17\x47\x5d\x52\xe4\x5c\x27\x4c\x8f\x49\x91\x2f\x70\xd3\x06\x97\x4a\x12\x2e\x3b\x09\xa6\xa0\x3c\x41\x3c\xd3\x7a\x62\x89\x1e\x1c\x23\x0d\x09\x8e\x95\xe2\x3e\x56\x8a\x5b\x31\xf1\x47\x76\x05\xb5\xc5\xae\x1c\x81\x4b\x2b\x56\x69\x44\xcb\x83\x12\x16\x6d\x26\x99\x3a\x62\xb8\x72\x0a\xb4\x16\x9d\x5a\xbc\x52\xba\xfc\x61\x88\x87\xa3\xbf\x99\x1d\x71\x97\x4d\xbe\x5f\xe6\x53\xce\x3e\x5d\x4f\x3e\x5d\x3b\xd3\xdd\x27\x7c\x24\x21\xa3\xde\xc9\xdf\xce\xd4\xe2\x9f\x9c\x27\x23\xa8\x70\xf4\xf7\x27\xa7\x6d\x79\x32\x82\x02\x47\x7f\xdb\x11\x3b\xc9\x12\x99\xc9\xea\xa6\x39\x9b\x9d\x51\xb3\xa4\x61\xe5\xee\x27\x8b\x29\x58\xbc\xf9\xfb\x53\x69\x35\x9f\x4a\xeb\xc9\x68\xf1\xc0\xfb\xba\xaf\xa3\xb0\x8c\x6a\xbf\xee\xaf\x8f\x24\x18\x4f\x3c\x43\x09\x6e\xa1\x2f\x45\x63\xce\x73\xa7\x44\x59\x9e\xcd\xce\x58\xac\xe3\x48\xdf\x0d\xe3\xc8\xf6\x7c\xaf\xbf\xf2\x18\x92\x16\x8a\x31\xee\x01\x09\xd8\x38\x7c\xda\x72\x75\x16\x0f\x8d\xef\x06\x22\xab\xb0\xba\x77\xad\x15\x79\xcf\x7c\xe3\x92\x3c\xef\x68\xec\x3f\x87\xc4\x34\x93\x21\xa6\x91\xf0\xb3\x5b\x4e\x6f\x2c\xc5\x04\xb6\xd7\xc8\x34\xb2\xfd\x7b\x05\x86\xeb\x50\x0b\x87\x7a\x88\xf1\x3d\x75\x19\x43\xca\x83\x2f\xfa\x8a\xd2\x50\x4e\xbc\x61\xb1\x24\x32\x06\x97\xb3\x52\x0c\x0c\x2b\xf1\x0d\x83\x93\x7f\xdf\xe6\x71\x6b\x0e\xb4\x71\xda\xef\x6d\xee\xc4\x98\xb7\x09\x17\x78\x8b\xae\x3a\xdd\x0f\xce\xec\xb2\xcc\xd3\xba\x12\xca\x07\x44\xf5\xfe\xf0\xc4\xdb\x7b\xb8\xa5\x2c\xef\xdf\x03\x30\xe1\x94\x24\x86\xe2\x16\x3e\x38\xb1\x90\xe9\x23\xd1\x40\x77\x1f\xa2\xe6\x03\xfd\x55\x49\xb4\x31\x57\x73\xf2\xd5\x7a\x56\x88\xf9\x87\x1c\x3f\x38\xf1\x6a\x8d\xdb\x34\xef\x41\xbc\x45\x0f\xa4\x02\xb0\x55\x58\xa1\xe6\xb7\xe9\x9b\x77\x2a\x6f\x8f\x1f\x9c\xf9\xfa\xb1\x9c\x44\xa1\x4a\x3b\x5a\xa3\x54\xf4\x44\xad\xd3\x54\xbb\xe9\x8c\x65\x58\x74\x77\x8b\x1e\xd9\x07\x8d\xe6\xe8\x86\xf3\xdd\x1b\xc8\x90\xc2\x23\xed\xc3\x65\x3b\x9e\x8b\xe8\x06\x99\x92\x2e\x41\x32\xda\x82\x73\x43\xa1\xa2\x4c\xb7\x25\xc7\x5c\x5e\xc9\xb9\x98\xff\x76\x83\xea\xf9\x57\x3b\xdb\x83\x57\xf7\x77\x06\xef\xe0\x2b\xdf\x02\xa1\xd2\xee\x62\x21\x8a\x0e\x96\x6a\xf8\x15\xc0\xfd\x47\x00\xba\xe0\x29\x80\xe2\x5b\x3d\x4b\x89\x4e\xe2\xdb\xaf\xa6\x3f\x05\xd2\x6a\x8f\x53\x3b\x49\xf3\xbc\xf8\xe7\x47\xbc\xa7\x26\x2d\x0a\x31\xab\x44\xf1\x61\x39\xcb\x90\xa2\xc1\x5f\x2d\xfc\xec\x91\x23\x0e\xdd\x7b\x10\xce\x8b\x23\xda\x82\x62\x97\x45\x25\x7e\x05\xeb\x80\xac\x08\xb2\xec\x91\x7d\x70\x1d\xf9\x67\x04\x58\x96\xc7\xa4\x87\xc4\xc3\x2d\x0d\x87\x9a\x63\xf4\xa8\x96\xfc\xd8\x3e\xff\x7a\xb8\x69\x6e\xb1\x4e\xa8\xdb\x3a\xbe\x1a\x6b\x58\x67\xb3\xb3\x47\xe6\xab\xa1\x65\x3b\x42\x2c\x66\x95\xbc\x12\xd8\xbe\x3c\x42\x70\x3d\xfc\x85\xab\x27\xfc\xb7\x28\xf2\xff\x09\x27\x17\x5b\xfe\x9f\xb8\x53\x9a\x91\x8a\xb2\x6c\x8f\x23\xfd\xe5\x71\x3c\x7f\xe4\x38\xf4\x82\xdd\xf4\xed\xb3\x48\x7f\x7d\x16\x87\xca\xde\xfe\xef\x87\xa1\x6e\x8e\xf0\x83\x53\xd6\x97\xf7\x40\xdd\x8d\x18\x14\x8c\x04\x4b\x47\xd5\x6a\xbd\x55\x62\x88\x5b\xbc\x9e\xa9\x5a\x9e\x61\xd2\x34\xc3\xec\xae\xfe\x54\x8e\x23\x19\xcd\xe1\xa6\xc0\x8a\x14\x98\x9d\x41\xe9\xac\xd3\xba\x64\x82\x07\xca\xaa\xa0\x3a\x41\x50\x39\xea\xd1\x0d\x2c\xb1\x74\x62\x58\xa0\x68\x55\x48\xda\x34\x43\x7d\xd1\x3a\x5c\x36\xcd\x70\xd1\x01\x5b\x46\xac\x85\x27\xb8\xaf\xd7\x5c\x44\xa5\xdf\xad\x3b\x5c\x6a\x57\x76\xab\xba\x60\x40\xcf\x0f\x67\xd1\xc0\xa8\xf4\xf7\x10\xbf\x46\xb6\xeb\xbb\xca\xd6\xa7\x58\xb1\x94\x2b\x3f\x56\xdd\x49\x2f\x7b\xbf\x2e\xc1\xd4\x8e\xb5\x1b\xc0\x6a\x74\xc3\x84\x47\x2c\x41\x3b\x81\x1c\x97\xdc\x67\x31\xa6\x90\xe3\x82\xac\x41\x21\xae\x44\x41\xb6\x0a\x32\x4c\xd4\x05\x6f\xbe\xb9\x03\xda\xea\xbe\xdd\x0a\x6a\x58\x8d\x2c\xe9\x6f\xad\xf9\x0b\x96\xf5\x77\xfb\x9c\x47\x89\x9f\x41\x82\x19\xba\x81\x0c\xb3\x20\xd3\x81\xcf\x72\x92\x4d\x87\xb8\x20\xad\xf9\xb3\x46\x7a\x7b\x41\x2f\x9b\xcb\x04\x0a\x7d\x73\x24\xaf\x78\x01\x0b\xcc\x41\x11\x40\x38\x25\xe1\xc5\xe4\x06\xbe\xad\x52\x15\x9d\xdf\xdb\xdd\x54\xeb\x9b\xe9\x49\xd1\xba\xb8\xd4\x94\xe1\x99\xed\x05\x32\x4c\xf4\xf5\xc8\x52\x5d\xb1\xbe\x58\xa8\xd0\x4b\x17\x5a\xc9\xa0\x30\xcd\x21\x75\x14\x53\x9a\x3c\xc5\x8c\x07\xb6\x4d\x4f\xb0\x9c\xc8\xa9\x85\x67\xb7\xf4\x6b\x23\xcd\x52\x77\x19\x14\x2a\xd3\x51\x04\xcb\x3e\x52\xb6\xed\xb8\xd7\xf8\xea\x94\x4e\x98\x80\x25\xc4\xdc\x57\x87\xa8\x4f\xcc\xf3\x3d\xd8\xba\xcc\x00\xa1\x14\xe1\x2a\x9f\xd7\x29\x09\xcb\x2a\x9f\x3f\xc2\xe1\xfa\xd6\x5c\xd5\x20\x6e\xcc\x9e\x77\x97\xb7\x87\xd2\x89\x9b\x66\x28\x9c\xb2\x69\x04\x89\xf6\x50\x17\x2e\x44\x1b\x06\xf7\xa9\xa9\x69\xa4\xea\x95\xdb\xbd\x92\xfb\xec\x10\xf1\xcf\x88\x15\x4a\x44\x94\xed\x86\x0a\x5f\x31\x09\x02\x5c\xd8\xe3\xaa\xa9\x80\xca\x29\x77\xb1\xe0\xfe\xa6\xeb\x4f\x0e\x52\x0b\x28\xab\x1c\x75\x51\xcb\x04\xd7\x36\x21\x23\x6d\x25\xe6\xa8\x9e\xfe\xa9\xef\xa0\xce\x5a\xfb\xbb\xda\x58\x92\xf4\x91\xfb\x31\x7f\x8c\x32\x1d\x5d\x20\xa7\x78\xb3\x95\xfa\xf1\xa3\x52\x9f\xff\x5a\xea\xf3\x87\x52\xdf\xed\xa9\x15\xfb\x1a\x55\x7c\xa8\xab\x40\x46\x37\x90\xa8\x70\x36\xed\xc5\xbe\x6e\x9a\x61\xa9\xc5\x9e\xb4\x4b\x7a\x77\x9d\xbc\x93\xf2\x44\x4b\x79\xba\x25\xe5\xf4\x4c\x6e\xa0\x1a\x48\xfd\x91\xf4\xdd\xdd\x5c\x89\x75\x8d\x15\xab\x39\x29\x36\x56\x92\x28\x27\xbd\x58\xe7\x58\xdb\x6d\xde\x2c\x0f\xdd\x88\x95\x58\x43\x81\x29\xf7\x59\x8e\x76\x0e\x05\x26\x1c\x8a\x8d\xcc\x06\xb9\x6d\x07\xc5\x46\x9c\xb7\xba\xda\x9b\xb9\xa4\x0b\x77\x32\x4c\xbb\x47\x37\xcc\xed\x4c\xd5\xdd\xa5\x40\xee\x69\x82\x05\x64\x98\xd3\xea\x6e\x90\x05\x3c\x47\x96\x4c\x6c\x3b\x9b\x62\x32\xc9\xa6\x56\x4a\x7f\x72\x3e\x3a\x6b\x5c\xa0\x86\x1d\x3c\xeb\xce\x35\x37\x4d\x96\xf4\x21\x57\xce\xc1\xb2\x4a\x0e\x24\x1f\x09\x94\x8a\x57\xfa\x3a\x00\x52\xf3\xdb\x27\xad\xcf\x59\x65\x3d\xf4\x49\x4b\x2c\x34\xd1\xfb\x0c\xaa\x18\xaa\xf4\xbd\x69\x7a\x43\xa4\x77\x57\xff\x30\x9d\x7f\xdb\x03\xa3\xcb\x39\x1b\x2a\x05\x0f\x62\xa8\x87\xb7\x59\x58\x4e\xc2\x73\xdf\xf3\xab\x50\xf6\x5e\x1f\x64\x58\xed\xde\x58\x24\x10\x72\x52\xb5\x5a\x23\xa8\x5a\x77\xaf\x52\xee\x5e\x46\xee\x9e\x4e\x63\x4a\x52\x0b\x95\x0a\xb4\xda\x3e\x0a\xb4\xfa\x5b\x4b\xd3\x2c\xc8\x05\x0a\x89\xb2\xe4\x5b\x0a\xcb\xe3\xa0\xcc\x9c\x2a\x7b\x78\x4c\xfc\x1f\x11\x15\xa6\x2b\x91\x44\xd3\xf4\xf9\xe3\xa7\x9c\x9b\xe6\x47\x56\xc1\xbf\xff\x2d\xac\xde\xd3\xba\x53\x60\xec\xc2\x73\xf0\x9e\xea\xca\xa7\xcc\xff\xca\xa1\xa2\x75\xd5\xa9\x3c\x24\xf9\x1d\x85\xa3\x6e\x75\x2e\xe0\x02\xbc\x67\x5b\xf4\xe4\x51\xd6\xca\xbc\xe1\x09\xc3\x52\xb5\x33\x2d\x2b\x67\xa4\x65\x32\xa5\x64\x4c\x93\xd9\x17\xba\x68\xe6\x82\x66\x94\xbb\xea\x1e\xc8\xf5\x3d\x52\x4a\x99\x3a\xff\xf2\x5b\x3d\x2b\xc4\xfb\x3c\xaf\x88\x01\xbe\x15\xd5\x63\xce\xfa\x03\x3b\x4f\x22\x58\x3a\x25\x45\x7a\xaa\x90\xea\x9d\xb5\x0f\x8b\x96\x5a\x86\xeb\x3c\xd5\xc1\x1e\xb1\x05\xd9\x65\x92\xcc\x64\x4b\xf4\xf4\x38\x32\xd9\xae\x0a\xeb\x69\x80\xea\x8f\xdc\x91\xeb\x27\x51\xa9\x10\x0c\x94\x7d\x55\xa9\x7f\xc2\x8b\x11\xe7\xba\x0a\x60\x8a\xe8\x8d\xdc\x88\x4e\x91\x25\x1c\x58\x57\xc6\x63\xc5\x7c\x67\x8c\xaa\x8a\x31\xd3\x35\x52\xb0\x0d\x20\xd3\x86\x9a\xc5\x96\xc7\x47\x63\x6e\x33\x37\x8c\x9b\x26\xde\x19\xd3\x30\x05\x31\x43\x4d\x4e\x9f\x91\x34\xde\x29\x75\x51\xe6\x39\xdb\xd4\x64\x6f\x2a\x2c\x85\xc1\x2d\x8f\x5b\x31\x07\xd9\x52\x20\xe3\xdc\xef\x9e\x53\xcb\x30\x48\x53\xd3\x79\x28\x43\xa9\xb2\x61\x90\x62\x6c\x2d\x61\x4f\x6d\x3f\x25\x83\x19\xe8\xfa\x57\x09\x64\x69\xf5\xd1\xd6\xda\x01\x7a\xc5\x4a\xa8\x61\x09\x9e\xba\x9c\x63\xb5\x13\xf3\x1e\x8d\x94\x6b\x37\xae\x60\xd2\x89\xf9\x76\xbb\xd2\x89\xd2\x11\x2f\x62\xd3\xb4\xed\x74\x0b\xf9\xd4\xde\x83\x94\x78\xdf\x38\x3c\x3c\x3c\x34\x14\x8f\xb2\xbc\x69\x8c\xfd\xf6\x95\xf3\x9f\x6c\x68\x65\x4d\x33\xb4\xb2\xbe\x10\xd9\x34\x8d\xa7\x06\x62\xd6\x55\x06\xba\xc4\xf4\xec\x23\x93\x20\x1d\x61\xbd\xb3\xc6\x40\x31\x27\x0e\x65\x8b\xbc\xe4\x8e\xf8\xc6\xca\xed\x6a\x85\x61\xae\x66\xd4\x50\xb7\x33\x5c\x0e\x75\xb7\xd7\x6e\x38\xff\x29\xb1\x6e\xe7\x2c\x2d\xdc\x87\x94\xfe\xe4\xe8\xdd\xf6\x81\x4d\xb7\xa4\x07\x5f\x5b\x33\xae\x60\x90\x15\xaf\xd3\xff\xc9\x4f\x6d\xeb\x80\xba\x04\xea\x4a\xa7\x50\x35\x57\x9f\xe3\xa5\x13\xc3\x05\x92\x1d\x3b\xb8\x63\xc7\x78\x97\x39\x3d\x37\xcd\x0b\x9d\x41\x32\xcd\x8b\xad\xcc\xe9\xf0\x92\x0c\xa7\xf6\x00\xce\x4d\x73\xa8\x47\x0c\x2f\x9a\xe6\x82\x7e\xf4\xdb\x79\x5f\x5f\x21\xda\xf8\x5f\x79\x27\xbb\x78\xe9\x94\x40\x90\x23\x5d\x6b\xe1\xea\xfa\x15\x97\xfb\xdb\xf5\x18\x1c\x44\x5b\x92\x56\xb1\x4b\x15\xc9\x58\x15\x13\x3a\x61\xda\x43\x49\x37\xb9\xb3\x05\x5e\xf4\x8f\x8a\xc7\x56\x78\x0e\xe7\x78\x01\x17\xb8\x82\x5c\x99\x15\xe5\xe4\x91\x49\x49\xad\x05\xac\x70\x32\x55\xb6\x6a\xb5\x55\x7e\x94\x17\xec\x1a\xcf\xe0\x0a\x5f\x92\xab\x1a\xd8\x76\x1e\xa2\x1b\x6c\x8a\xe4\xd7\x78\x31\xc9\xa7\x3b\x57\x30\x57\x0f\xa3\xab\xc6\x85\x12\x53\xa8\x31\xb7\xca\xa0\x0e\xf3\x80\xc7\x78\xae\xee\x4d\x76\xae\x60\x89\xe7\x93\x52\x0f\x4a\x70\xbe\x1b\x5b\xcb\xdd\x35\xc4\xb8\xde\x8d\xad\x64\xe7\x6a\xf7\xca\x5a\x4d\xea\xa9\x55\x40\x81\x2c\x1e\x5d\xab\x1b\x82\x84\x46\x73\x6b\xbe\xbb\x84\xd5\xa4\xb6\xed\x29\xc6\x3b\xd7\x01\x8d\xc3\xa2\x63\x87\x22\xb2\x2c\xe9\xaf\x7a\x67\x90\x6c\xdb\x0a\xa4\x66\x8b\xb6\x6c\xed\x1f\xaa\xf6\xc1\xbd\xcb\x41\x8f\x94\xfb\xf3\xed\x52\x39\x7d\x51\xa8\x5c\xa4\x0c\x1f\x2a\xf8\xe7\xbd\x82\x07\x11\x91\x41\xa0\xe5\xfc\x4a\xa3\xb2\xa5\x4b\x1e\x0f\xcb\x3e\xb7\xa1\xd8\x83\xfb\xc9\x43\x1e\x91\x65\xf1\xda\x85\xa9\x41\x83\x54\x95\x77\xff\x37\x60\x63\x57\x03\xeb\xcc\x54\x07\x73\xec\x76\x30\x55\x0d\xdf\xa3\x14\xfb\x25\x4c\xef\x17\x30\x3d\xa5\xc3\x75\x9c\xbb\xe5\x36\x3a\xe5\x3a\x95\x95\x2e\x4d\xcf\xd1\xfa\xcb\xe9\x0b\x79\xa0\xa6\xd7\x87\xb5\x3c\x50\x62\x37\xaa\xab\xe2\x21\x4f\x90\x84\x25\x45\x39\x51\x25\xda\x5d\xfc\x0d\x33\x8c\xa3\xa4\xd7\x5b\x7e\x02\xcb\x4d\xf9\x53\x1b\xe6\x14\x98\x93\x27\x07\x35\x16\xb0\xb4\xb1\xe0\x90\x87\xae\x69\x2e\x43\xb7\xe3\xee\xe5\x4e\xde\x34\x39\x24\x38\x6b\xbf\x89\x60\x2e\x14\x3c\x58\x86\x45\x50\x58\x98\xf3\xc4\xc2\xd2\xea\xfb\x0a\xc8\x79\x50\x87\xaa\x7c\xbe\xed\x50\xcb\x17\x9c\x43\xac\x6a\xea\x0d\xdb\xb0\x12\x7e\x5b\x61\x1a\x25\xd6\x5f\xce\xfd\x12\x27\x8b\x82\x44\xeb\x2f\xe7\x41\x59\x12\x8f\xd2\x4d\x66\x72\xeb\x4b\xa1\x4f\x9f\xe6\x3f\x0d\xab\xb6\x8c\xdb\x4f\x9f\x7e\x33\xc0\x58\x18\x1c\x8c\x27\xa6\xf1\x00\x46\xb7\x02\xf7\x53\xee\x27\x9b\xc2\x5c\x7d\xd8\xed\xd0\x47\xdd\xbe\x7b\x4a\x13\xbf\xc0\x42\xab\xca\x35\x2e\x9c\x18\xe6\xfd\xbd\x3a\xac\xb0\xda\xbc\x5c\x63\x72\xe7\xc6\xbd\x67\x17\xf6\x05\x87\x1e\x94\xd8\x97\x62\x7f\xc1\x25\xb0\x21\xa3\x48\x5e\xe5\x70\x18\xe7\x4d\x53\x3a\x69\xc5\xbe\x29\xe3\xa2\xcb\x23\xc6\x60\xac\x66\xdf\x07\x73\x91\xe5\x2b\x99\xd1\x56\x06\x86\xc5\x96\x91\x71\xaf\x06\xf8\xb1\x12\x60\x81\xc3\xa5\x69\xaa\x84\xcb\x47\x56\x82\x76\xcc\x3c\xee\x2c\x2a\xc1\xbe\xf1\xa8\xf4\x3b\x37\x74\xdd\xc7\xfe\xdb\x65\xe8\xda\x5c\x17\x6c\x4d\x7c\x3a\x77\x04\xf6\x89\xa3\x85\x23\x6c\x0f\xe6\xca\xaa\xe3\xfb\x09\xab\x31\xdf\xb9\xe1\x2f\xdc\xe8\xc6\xaa\xfd\x7a\x4a\x0b\x0b\xda\x4b\xbc\x5a\xb3\x39\x0f\xdd\x88\x82\x85\xb9\xbf\xf2\x4b\xa8\xf1\x07\xfc\x20\x6f\xa3\x27\x45\xcc\x21\xd1\x90\xdc\x20\x45\x32\xf7\x73\x95\x1d\x54\xb2\xa2\x5c\x80\xb4\xb5\x92\xd7\x9c\x83\x37\xa4\x10\x68\xb5\xa6\x08\x89\x57\x78\x0d\xd7\x28\x61\x85\xc9\xdd\x91\x12\x57\x9c\x22\x17\x09\x73\x2c\xdb\x90\x6a\xd3\x37\xe7\x14\xdc\xc8\x4e\xef\x49\x7c\xc5\x44\x17\x4b\x72\xb8\xd6\xab\x27\x1d\xcc\xce\xa4\x13\xc4\xaa\x43\x49\x6e\xa1\x94\x38\x25\xae\x9c\x12\x17\x4e\x09\xf9\x2e\x8e\x21\xc3\x57\x8c\xac\x6b\x0e\x5f\x79\x0b\x77\xc1\x9d\xd9\x65\xc9\xb8\x42\xfd\x15\x4b\xa0\x7a\xac\x97\xbf\xf0\xa2\xc9\x6a\xeb\x0c\xe0\x7a\xeb\x65\xea\x4f\x92\xed\xbe\x6a\xbb\x0f\x7e\x60\xad\xdd\xf9\x2a\xd7\x35\xc2\x0f\x23\xdf\x2d\xc7\xda\x12\x4d\x43\x06\x38\x72\x77\x85\xa3\xf3\x41\x7a\xee\xbb\xfc\x5a\xa5\x15\xd7\xf9\xf5\x2f\xa2\xa1\x55\x57\x4d\x65\x09\xde\xa5\x07\xc8\x41\xe8\x5d\xf5\xf1\x1e\x18\xa2\x55\xf7\xaa\xfe\x67\xd8\x65\x35\x99\xe0\x4d\x53\x84\x17\x14\x03\x8d\xd0\xe5\x4d\xb3\x9e\x15\xa5\x38\x4e\xf3\x59\xc5\x04\x57\x72\x32\x64\x02\x09\x9d\x7b\x37\x0d\xca\x8f\x5d\xe7\xd7\xcc\x92\x20\x78\x97\x61\xf9\x3d\x9a\xb3\xdf\x47\x37\xd6\x98\xfb\x2e\x6c\xa4\xb0\xad\x48\x2d\x76\xc6\xea\x57\x5d\x8b\xb4\x6e\x19\x0c\x2b\x27\x6e\x2b\x45\x33\xd3\xac\xfa\x6c\xa8\x0a\x8c\x36\xaf\x98\x71\x5d\x1e\xbc\x62\xc5\x68\xcc\xa1\x2b\x5a\x0e\x24\x6e\x7c\x3c\xc8\x4c\x53\xa5\x35\xe4\x5d\x30\xf2\x0e\x98\x3b\xd9\xf8\x0a\xbf\x39\x73\x79\xc5\x2a\xce\x21\x53\x56\xf2\x77\xf8\xda\x5b\xc9\xbe\x48\xfc\x9f\x9b\x35\x55\x15\xb7\xff\x2b\x33\x0d\xe3\xfd\xf6\x60\x35\xa7\x3c\x76\xa6\x5d\x7c\x5b\x11\xff\x62\xe5\x88\x60\x2b\x28\x45\xc4\x3c\x92\x14\x6c\x18\xdd\x1d\x99\x01\x6e\x28\x55\x14\x49\x6a\x9d\xbc\xfd\x0c\x8d\xb3\xd9\x99\xe1\x2b\x57\x9c\xe8\xdb\xfb\x07\x2d\x92\xea\x0b\xd3\xf1\xd3\xee\x13\xd3\xe8\x35\x4b\x59\x06\x39\x07\xb7\x11\xe0\xb9\x20\xb9\xff\x5b\x88\x64\x73\x42\x7c\x12\x25\xaa\xcf\xef\x86\xd0\x62\x55\x17\xd1\xf5\x8b\xb6\xcc\x5e\xd4\x59\xdc\x66\x7b\xd4\xf3\x3f\xbf\x0b\xd0\xf7\x0f\x57\xb3\xb4\x16\xe7\x09\x4d\xcf\x7f\xbf\x38\x7f\x24\x13\xae\x53\xdb\x1b\x51\xbb\xdd\xd0\xbf\xab\x3a\x25\x75\x3e\xdb\xd4\x4b\x54\x9b\x58\xd6\x6d\x7a\x6a\x8a\xd0\x6d\x1a\x81\x88\x59\x94\xf9\x99\xed\xdd\xa9\xaf\xd8\x54\x56\x68\x21\xf3\x40\x6e\x8a\x50\x72\xf5\x9d\x8a\x65\x18\x81\x0c\x8b\xd6\x03\xcd\x50\xa8\x6c\xa3\x65\x18\x50\xe1\x8d\xdd\x7f\xcb\x51\xd9\x76\x90\x51\xf4\x67\x65\x3c\xc8\x2d\xcc\x6e\xdb\x42\x90\x3b\x5f\x25\xe6\x77\xbf\x4a\x94\x3c\xe8\xdd\xc0\x7c\xf3\xbd\x9f\xe5\x35\x8d\xc7\x37\x88\xca\xfb\xb9\x41\xe1\xc4\x90\x53\x54\xa4\xbe\x29\x2a\x49\xa7\x3b\xa5\xaa\x9f\xa1\x18\x2f\x73\xc4\x56\x96\xea\x61\xa6\xc3\x34\x87\xca\x89\x29\x30\x37\xcd\x61\xae\x8a\xba\x9a\xa6\xbf\x0d\xab\xa2\x22\x72\x7d\xbb\xf4\x6b\xe5\xb8\x0c\xb1\x87\x51\x6b\x00\x6e\x58\x43\x81\x09\x62\x0a\x43\xd9\x34\xc3\x9c\xf7\x5e\xb1\xeb\x0f\xe5\xdf\x95\x2e\x6b\xb9\x73\xc5\x96\x84\x69\xd7\xae\x8b\x8b\x58\xd2\xa7\x5c\xf8\x0b\x96\xf6\x74\xe2\x51\xe2\x93\x33\xef\x06\x65\x58\x07\xb5\xce\x22\xcb\x49\x3d\x1d\x62\x3e\xa9\xfb\x60\x9e\x5a\x42\x6a\xe8\xa0\xf6\x9f\x49\x63\x1a\xb9\xfe\x66\xb9\x0d\x15\xf3\xbb\xb7\xb7\x4c\xe8\x8f\x7f\x42\x72\xa6\xab\x10\xb7\xaa\x7d\x6a\x62\x8c\xf6\xa3\xbf\x89\x2e\x8e\x1c\xa8\x52\xb8\xa9\x81\x78\xae\xde\x37\xe5\xe7\x3d\x8b\xea\xef\x91\xc4\xd6\xb9\x95\x0f\xbe\xff\x21\xf7\x46\x45\x5b\xb5\x2a\x94\xef\xbf\x77\xa2\xbd\xb6\xdf\x80\x6e\x38\x46\xda\x76\x90\x4f\xe4\x74\x17\xb3\xb6\x1e\x6c\x52\xa0\x3b\xb5\xf0\xbc\x4f\x03\x88\x2e\x30\x26\x42\xf1\xa0\x78\xd1\x4f\x2e\x2c\x8b\xe7\x93\x62\x1a\x56\xea\x6b\x5d\xad\x53\xf2\x49\x61\x79\x24\xce\xfa\x01\x5d\x0e\xfa\xc9\xa2\xae\xe9\xa8\x6a\x5c\x6a\x98\xee\x60\xd5\xeb\xcf\xed\xbb\x80\x7e\x67\xc9\xb6\x7e\x64\x9b\xaa\xa2\x48\x6c\x22\x75\xcb\x70\x0c\x4b\x6c\x5c\x62\xc1\x2d\xe6\x86\x59\x64\x90\xdf\x24\x2c\x83\x5b\xd9\x06\x60\x7a\x87\xc5\x75\xd9\x5a\xd6\xb9\xc5\x86\xeb\x18\x81\x65\x65\xe4\x04\xab\x6f\xd0\x04\x16\x96\xe8\x0b\x0c\xab\x8d\xc8\x5a\x56\x16\x56\x9b\x69\x06\x64\x36\x56\x81\x6d\x6f\x4d\xb5\xb0\xd0\x33\x2b\x65\x33\x36\x75\x65\xfa\x93\xf7\x2d\x9c\x33\xbe\x89\xd1\x36\x98\xc6\x1b\xe6\x18\x08\xbc\x63\x48\x81\x2c\xf4\x9c\x09\xee\xaf\x88\x0f\x68\x33\x33\x1d\xf7\xeb\x6a\x87\x4f\x73\x8b\x7d\x72\x3e\xcd\x77\x79\xd4\xd0\xaf\xc5\x99\x98\x58\xf6\x34\xa2\xc7\xe8\xc9\x88\xdc\x26\x65\x70\x63\x21\x53\x58\xe9\x67\x75\xd5\x0a\xd7\xd8\x56\xeb\x0e\x2e\xf3\x3c\x15\xb3\x6c\x90\x17\x83\x4b\x99\xcd\x8a\x9b\xc1\x9c\xc2\x4d\x03\xae\x50\x7f\x49\x25\xb3\xc5\x60\x95\xcf\x85\x01\x97\xdd\x87\xe9\x03\x62\xd4\xc1\x72\x56\x0e\x56\x79\x21\x06\xd5\x72\x96\x0d\xbc\xa7\x83\x52\x2e\x32\x99\xc8\x78\x96\x55\x1a\x48\x69\xc0\x39\x1a\xae\x37\xde\xdb\x7f\xfa\xec\xe0\xf9\xe1\xec\x32\x9e\x8b\x64\xb1\x94\x5f\xbe\xa6\xab\x2c\x5f\x7f\x2b\xca\xaa\xbe\xba\xfe\x7e\xf3\xe3\xe5\x6f\xaf\x5e\x1f\x1d\xbf\xf9\xd7\xc9\xef\xff\xdf\xe9\xdb\xb3\xf3\x77\xff\xff\xfb\x8b\x0f\x1f\xff\xf8\xf3\xaf\xff\xfa\xef\x27\x9f\x0d\x38\x43\x4f\x78\xfb\x70\x83\xde\x3e\x5c\xdc\x2f\xec\xf5\xe0\x3d\x4e\x3c\x32\x3f\x9e\xeb\x82\x27\xf6\xc0\x13\xfb\xe0\x89\xa7\xe0\x89\x67\xe0\x89\x03\xf0\xc4\x73\xf0\xc4\x21\x78\x82\x06\x09\xcf\xa3\x3f\x63\xfa\xb3\x37\x85\x97\xea\x43\x8e\x23\xf4\xc4\xa1\xfa\xa2\x4a\x55\x51\x1a\xdd\xf1\x6c\x8a\x9d\xe7\x22\x91\x99\x30\x4d\xfd\xeb\xcc\x56\x73\xae\x1f\xd9\x43\x53\x33\xbb\xdd\x7c\xb7\x69\xd4\x99\x1e\x37\xdf\x54\x7f\xab\x0b\x1b\x61\x9a\xfa\xd7\x21\x2f\xab\xa8\xf4\x05\xc0\xdd\x26\x9c\xc1\x70\xc9\xab\xe2\xe6\xe7\x12\x0b\xf1\xad\x96\x85\x60\x6d\x3d\xa8\xc1\x6f\xe3\x59\x15\x2f\xd9\x6b\xfe\xf3\x56\x73\xa0\x70\xfa\x2f\xcb\x70\x76\xdb\x66\x05\xfe\x63\x34\xfa\xcf\x41\x99\xd7\x45\x2c\xde\xce\xd6\x6b\x99\x2d\x3e\xbe\x3f\xc5\x79\x1e\xdf\xf9\xf7\x1a\xce\x6a\xb6\xfe\x8f\xff\x17\x00\x00\xff\xff\x2f\x88\x72\xca\xa2\x43\x00\x00")
+var _bignumberJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\x7b\x7b\x77\x9b\xc8\x93\xe8\xff\xfb\x29\x24\x6e\xcc\xe9\x36\x05\x02\xd9\x89\x63\x48\xc1\xc9\x24\x76\x7e\x9e\xeb\xd8\xb9\x71\x32\x33\xbb\x8a\x26\x47\x46\x8d\xd4\x09\x02\x85\x87\x1d\x27\xf8\xf7\xd9\xef\xa9\x6e\x40\xf2\x23\xbb\xb3\xff\x48\xd0\x8f\xea\xea\xea\x7a\x77\x31\xda\x1d\x5c\xca\x45\x56\xaf\x2e\x45\xe1\x7c\x29\x07\x57\x63\xc7\x75\xf6\x06\xcb\xaa\x5a\x97\xfe\x68\xb4\x90\xd5\xb2\xbe\x74\xe2\x7c\x35\x7a\x2b\xbf\x8a\xb7\x71\x3a\xda\x1e\x3e\x3a\x3d\x79\x75\x74\xf6\xea\x68\xb0\x3b\xfa\x8f\xd1\xee\x60\x95\xcf\x65\x22\xc5\x7c\x70\x79\x33\xf8\x21\x52\xb9\x18\x54\xf9\x20\x91\xdf\x1f\x03\x57\xe4\x97\xa2\xa8\xbe\x16\x72\x25\xb2\x51\x5e\x55\xf9\xff\x29\xc4\xa2\x4e\x67\x85\x2d\xbe\xaf\x0b\x51\x96\x32\xcf\x6c\x99\xc5\xf9\x6a\x3d\xab\xe4\xa5\x4c\x65\x75\x43\xcb\x0c\x93\x3a\x8b\x2b\x99\x67\x4c\xf0\x9f\x46\x5d\x8a\x41\x59\x15\x32\xae\x8c\xa0\xeb\x18\xa8\xae\xfe\x6d\xc6\x04\x64\xfc\xe7\xd5\xac\x18\x54\x50\x80\x84\x1c\x6a\x28\x21\xc1\x6a\x29\xcb\x40\x26\x6c\xc8\x92\x81\xcc\xca\x6a\x96\xc5\x22\x4f\x06\x33\xce\x0b\x51\xd5\x45\x36\xf8\x62\x9a\xa7\x6c\xfc\x0c\x8c\x38\xcf\xca\xaa\xa8\xe3\x2a\x2f\x06\xf1\x2c\x4d\x07\xd7\xb2\x5a\xe6\x75\x35\xc8\xc4\xb5\x01\x82\x43\x26\xae\xdb\x75\x08\x60\x56\xa7\xe9\x10\x33\xd3\xfc\x17\xcb\x60\x0c\xcf\xf6\xe1\x2d\x18\x97\xb3\x52\x18\x9c\xff\xa4\x7e\x74\x9b\x0c\x4a\x14\x96\x61\x80\xe7\x22\x66\xdd\x8a\x09\xb6\x90\xee\x20\x14\x09\xbf\xe4\xf0\x91\x25\xf0\xce\x4a\x1c\x61\x79\xf0\x55\xad\xc3\x72\x34\xf4\x51\x18\x88\xd5\xcd\x9a\x06\x0b\x6e\x9a\xee\xae\x18\xa2\xdb\x34\x43\x02\xf6\x5e\x2c\x8e\xbe\xaf\x99\xf1\xb7\x1d\x19\x16\xab\xd0\x98\x18\xd6\xb9\x53\xa6\x32\x16\xcc\x85\x8c\x5b\xc6\xd4\x32\xb8\x65\xb0\xc8\xff\xf4\xc9\x31\xac\xca\x32\x78\xf4\xc4\x80\xbd\x83\x30\x8b\x0c\x69\xf8\x86\xc1\x9d\x4a\x94\x15\x2b\x7b\xc2\x2c\x58\x02\x25\xe4\xb4\xdd\x3c\x62\x89\x53\xa2\x1b\x7a\x23\x11\xb1\x12\xcb\x16\xb4\xc7\xc1\xf6\xb8\xef\xc1\x17\xd3\x2c\x9d\x42\xac\xd3\x59\x2c\xd8\xe8\x6f\xf7\x93\xe3\xee\x36\x9f\x9c\x11\x10\xdc\x54\x64\x8b\x6a\x19\x7a\x4f\x89\xd2\x6f\xe1\x92\x68\x99\xe3\xd0\xe3\x3e\x01\xdd\x7f\x8a\x88\xa5\x13\x2f\x67\xc5\xab\x7c\x2e\x5e\x56\xcc\xe5\x8f\xae\x51\xe2\x6b\x56\x82\xe7\x42\x06\x89\x53\xf2\x5b\x91\x96\x82\x08\x7d\x97\x8c\x3d\x91\x9d\x12\x85\x53\x42\xe2\x08\x14\x8e\x80\xc4\x89\x91\xd1\x63\xcc\x23\xd1\x82\xe6\xbe\x80\xab\x5c\xce\xd9\x5b\x74\xff\x27\x5a\x23\xba\xea\x58\xb7\xe8\x20\xd0\x16\x2d\x6e\x02\x11\xff\xfd\x6f\x62\xc8\xbc\x60\x05\xba\x20\x51\x04\x32\x44\xcf\x0d\xe4\x08\x3d\x17\x0a\xcb\xe2\x41\x8f\x9a\xc0\x42\x21\x34\x11\xd3\x0d\x02\xb7\x9a\x57\xfa\x7d\x0d\xd7\xed\x89\xa8\xe6\xfb\xc7\xc2\x83\xff\x0d\xf1\x6e\x6f\x09\x31\x56\x60\xe9\xc8\x6c\x2e\xbe\x9f\x27\xcc\x70\x0c\xce\x43\xdb\x33\x4d\x35\xbe\x3b\x3c\xc3\x31\xe8\xd0\x38\x30\x49\x50\xc4\xac\x88\x97\x6c\x24\x46\x92\xf3\xd0\x8d\x98\x1b\x16\xa6\xc9\x0a\x94\x1c\x0a\x0b\xad\x6e\x1d\x69\x79\x1c\xd4\xb2\xf5\x25\x49\x6a\xb6\x60\x2e\x48\xce\xfd\x6e\x7c\xd9\x72\x01\x07\x89\x6e\xb0\xff\xfc\x3e\xda\x92\x07\x92\x48\x44\x68\xd6\xfd\xe8\x47\x06\xda\x76\xcd\x03\x75\x58\x9b\x5d\x4a\xa8\x2d\x8f\x73\x99\x6c\x4d\x85\xdc\x34\xbf\x98\x66\xbd\xc5\x76\x89\x53\xee\x0a\x0e\x05\x16\xb6\xb4\x3d\x28\xc2\x1f\x9c\x0e\x81\x8e\x83\x84\x39\x20\xc2\x0f\x64\xc2\xde\x84\x85\xea\x98\x50\x8f\x3b\x0d\xba\x03\xd9\x3a\xb7\x29\x48\x64\x85\xe5\xf1\x9d\x1b\xd0\x5b\x94\x16\xde\x70\xa8\x43\xa9\x79\x40\x9a\x66\xe2\xc4\xce\xba\x2e\x97\xac\xa7\x92\x22\x09\xd4\x36\xde\x04\x75\x28\x03\xfe\x70\x84\x04\x05\x87\x07\x5b\x9b\x23\x92\xdd\xd8\xdd\xbe\x6e\x35\x96\x36\xd6\x8a\x56\x81\xb4\xed\xa0\xb4\xd0\x70\x0d\xe2\x88\x1e\x9e\x16\x8f\xc1\xf6\x36\xde\xa2\x7b\xdb\xeb\xcb\xd7\xa4\xc7\xa0\x02\xa9\x75\x26\x69\xcb\x04\x62\x58\xc2\x02\xd6\x30\x47\x71\x87\x4d\x60\x85\xef\xe0\x1a\xbf\xaa\x15\xf7\x0e\xc2\xca\x34\x95\x28\x55\xf9\x69\x7e\x2d\x8a\x57\xb3\x52\x30\xce\x61\x1e\xa2\x6b\x9a\x2c\xc1\xdf\xe1\x77\x74\x81\x46\xdc\xe3\x2a\x58\xb7\xaa\xaf\xe2\xb0\xc4\xb5\xb3\xce\xaf\x99\x68\x37\x66\xcf\x39\xfc\x8e\x09\xac\x9d\x18\x4b\x96\xb2\x82\x2d\x9d\x98\xc3\xd2\x11\x5c\x09\x3d\x87\xb5\x23\x70\xed\xc4\x3d\x27\x2d\xb0\x64\x02\x2a\xea\xaa\x31\xc1\x45\xc7\x34\x2e\xe2\x62\x62\xdb\xc9\x34\x58\x38\xeb\x7c\xcd\xb8\x62\x97\xe1\x62\xe2\x4e\x5b\x21\x32\x5c\x83\x9a\xdc\x70\x1e\xd9\x76\xed\xd3\x4a\xb8\xa0\xa5\xb0\x86\xa5\x53\xa2\x84\x25\xbe\x62\x4b\x58\xc3\x0a\xae\x09\xfe\x02\x97\x4e\x0c\x31\x2e\x9d\x02\x6a\x54\x38\xe5\x58\x5b\x2b\xcb\x83\x39\x2e\x26\xf9\x14\x12\xcc\x46\x63\x88\x31\x6e\x1a\x37\xcc\x9b\x46\x9b\x87\xc5\x24\xb7\xbc\x29\xc4\xb8\x1f\x5e\x47\xad\xc9\x98\x37\x4d\xcc\x4d\x93\xb9\x88\xd7\x4d\x73\x8d\xc8\x96\x4e\xf9\xc2\x8d\xf6\xfc\x31\xe7\xfe\x3c\x4c\x9a\x66\x8e\x98\x98\x26\xdb\x57\x23\xe2\xa6\x79\x86\x78\x6d\x9a\x9e\xb9\x98\xe4\xb6\x37\xdd\x9e\xf4\xdc\x3f\xe0\x1c\x3c\x5a\x51\x6f\x50\x60\x1c\xa5\xcc\xf0\x0c\xb0\x57\xdc\xa7\x8d\x76\xec\xdb\xd1\x07\x73\x88\x39\x9d\xa4\x6d\x67\x81\x65\x11\xa9\xf2\x69\x98\x05\x9c\xf6\x81\x2e\xe4\x4d\xc3\x2c\xab\x86\x85\x53\x67\xe5\x52\x26\x15\xf3\xb8\x16\xcc\x2d\x1a\x0f\x5b\x0a\xeb\x8e\xb9\x3a\x6e\xc3\x08\x92\x10\xe7\x81\xb0\xf0\x5c\x89\xec\xcb\x8a\x2d\x26\x73\xcb\x9a\x72\x1e\x08\x4c\x99\x80\x9a\xdf\xb6\x6a\x4c\x6c\x78\xf1\xf3\x43\x5e\x2c\x89\x17\xe9\x88\x2a\xd4\x44\xab\xc8\xce\x56\xe0\xc2\x73\x90\x70\xc5\x23\xb7\xa9\xfc\xaf\x30\x24\x75\xde\x01\x74\xaa\xfc\x42\xab\x1e\x75\xde\x39\xe9\xfa\x89\x3b\x25\x13\xec\x08\x20\x30\x64\x83\x97\x58\x30\xa1\x18\x0b\xbd\x43\x44\xd9\x34\xe3\x7d\x44\x69\x9a\xbf\x85\x58\x46\x09\x5b\x42\xc9\xfd\x54\xfd\xf5\x0a\x41\xe0\x47\xd6\x9a\x6c\x4e\x98\x12\xbf\x11\xcc\x1e\x16\x31\x46\xab\x76\xee\x02\x65\x75\x88\x59\xd3\xfc\x16\x62\xcd\xb5\x62\x08\xb2\x30\x0e\x96\x4a\x60\x21\x26\x8d\xb7\x44\x5a\xb4\x6e\x05\x96\x1c\x07\x1b\x4b\x58\x62\xaa\x5a\x49\xb3\x85\xb1\xb2\x3c\xb6\x9d\x85\xae\x3a\x38\x9a\xee\x18\x41\x66\xdb\x2d\x24\x1e\x6c\x66\x5b\x58\xdb\x31\x74\x43\x6b\xcb\x43\x8c\x4d\xb3\x9d\xc3\xef\x4c\xea\x29\x57\xbe\x70\x4d\x33\x8f\x0c\xdb\xb0\x96\xfe\x72\x73\x32\xdf\xef\x79\x55\xe8\x6a\x05\xcd\x04\xb1\x9a\xd6\x02\xf4\x04\x55\xe7\xd2\xd0\x5b\x60\x59\xf2\x45\x27\xd6\x81\xc2\xbd\xe8\xfb\xe5\x94\xc3\xb0\x70\x4a\xfe\xb3\xc2\x22\xb8\x2c\xc4\xec\xeb\x6d\xe6\x90\xbf\xc5\x2a\x28\x08\x66\x85\x45\xcf\x25\xd5\x06\x97\xe3\x96\x4b\x0a\xe2\x13\xdd\xcd\xb2\x50\x34\x8d\x08\xab\xa6\x11\x43\x8c\x99\xe0\x9c\x74\x7d\x01\x4c\x36\x8d\x31\x17\xb1\x5c\xcd\xd2\x81\xd2\x40\xa5\xc1\xad\x7e\x78\x64\x0c\xc8\xaf\xcb\x93\x41\x31\xcb\x16\xc2\xf0\x8d\x41\x96\x57\x83\x59\x36\x90\x59\x25\x16\xa2\x30\x38\xf9\x28\xc3\x2d\x7d\x79\xa2\x57\xd7\x67\x48\xf4\x28\xd0\x03\x89\x59\x2f\x0f\xd9\xc4\xb6\xe5\x34\xc8\x3a\x8d\xa3\x8c\x00\x66\x13\x77\xfa\x2b\x3f\x80\x36\x6a\x55\xbb\x37\xb6\xc7\xc3\x1f\x91\x70\x62\xe2\x29\xc5\xee\xfe\x9b\xb0\x52\x0d\x13\xa1\x54\xb7\xcf\xe8\xbf\x02\x6a\xca\x38\x6c\x89\xce\x69\x87\x96\x46\x89\x88\x7c\x54\x14\x79\xc1\x26\x06\x3d\xff\x26\x17\x67\xda\x9d\x01\x23\x5e\xad\x0d\xe5\xe4\x26\x72\x61\x80\x31\x97\x57\xfa\xf7\x43\x7e\x92\x55\x06\x18\xe2\x9b\x01\xc6\xa2\x52\x3f\xc2\x00\x23\xad\xd4\x0f\x3d\xae\x64\x56\x97\xf4\x9f\xcf\x0d\x30\xd6\xa9\x7a\x59\x17\x22\x96\xe4\xbf\x1b\x60\x14\xb3\x6c\x9e\xaf\xe8\x21\xaf\x33\x1a\xa3\xf4\x86\x01\x46\x25\x57\x82\x06\x57\xf9\x6b\xb9\x90\x95\x7e\x3c\xfa\xbe\xce\x33\x91\x55\x72\x96\xaa\xf7\x63\xf9\x5d\xcc\xf5\x53\x5e\xac\x66\x95\x7e\x2c\x66\x6a\x8b\xb4\x52\x7e\xad\x9a\xde\x6d\xad\xd8\xc9\xba\x01\xc6\x66\x93\xd3\x89\x98\x5a\x06\xe3\x03\xc3\xca\x2c\xc3\x1f\x18\x56\xc5\x83\x6a\x59\xe4\xd7\x83\xc2\xc9\x66\x2b\x81\x9b\xc1\x9a\x4e\x06\xbc\x45\x17\x8a\x0d\x41\x3f\x76\x5c\xa6\x49\xda\xc7\x11\x90\x42\x0c\x33\x52\x29\xb0\xc4\xf7\xa4\x5f\x66\xfc\xa7\xf0\xb5\xad\x47\x72\x4e\x67\x74\xd4\xa5\x3a\xea\x52\x1d\xb5\xf2\x67\x94\x22\xca\x6c\x09\x6e\x98\xf3\xdc\xc2\x1b\xa8\x31\x83\x04\x67\x93\x14\x5d\x32\x0c\xc9\x68\x39\x91\x76\x6d\x7b\xd3\x1d\xcf\x6d\xdc\x5e\xe7\xa4\x38\x67\x2c\xb7\x3c\x3e\xba\xe1\x90\x86\x38\xeb\xcc\x9e\x72\x0d\x0b\xae\x24\x67\x20\xb4\x13\xd0\x75\xbe\xc0\x34\x98\x69\x17\xc0\x25\x1e\xc4\x58\xb9\xa2\x1e\xe4\x3b\x6a\xe5\xdc\xbe\xb1\x3c\xed\x60\x6a\x7d\x4e\x68\xa7\xe4\xcc\x78\x0f\x51\xdf\x9a\x23\x21\x46\x37\xac\x23\xd7\xbf\x87\xee\xad\x92\xed\x82\x6c\x5e\xd6\xd9\xbc\xd9\x24\xb5\xc8\x48\x31\x9a\x91\xf8\xc9\x4e\x37\x83\x5c\xaf\xfd\xb0\x8a\x58\xdc\x34\x45\x6b\x01\xab\xa6\xa9\x10\x99\xd8\xb2\x80\x71\xf8\xb4\x69\x9e\x6a\xad\xb5\xaf\x46\x14\xca\x02\x92\xd7\x91\x87\x6e\x54\x87\x6e\xd4\xa2\x31\xf5\x5d\x7f\x36\x49\x09\xf6\x8e\xe7\x9a\xde\x36\xb0\xce\x32\x66\x4d\x33\x9c\x6d\x4c\xff\xa0\xa3\x15\x9d\x7b\x44\xca\x56\xa8\x60\x8b\x86\xe0\x72\x92\xed\xdc\x4c\x81\xa4\xcd\xce\x9a\xc6\xe5\xbe\x6a\x56\x52\x08\x42\xb9\x0c\x88\x79\xc4\x7a\x18\x29\x94\xe8\x41\x6a\xdb\xdc\xdf\x6a\xb4\x88\x1f\x96\x93\x1b\x3b\x9f\x02\xd1\x17\x09\xe5\x15\xeb\x90\xde\x59\x4e\xea\x29\xdf\x2d\x7d\x97\x43\xa1\xb4\x74\xa0\xb5\xa4\x8b\x98\x6a\x0d\x93\xa3\x07\xb5\x66\xa9\x5a\x9d\x4b\xad\xce\x25\xdf\xb8\xc8\xd4\x67\x61\x49\xeb\xdf\x19\x52\xaa\xa3\x1b\x62\x49\xda\xd9\x11\x96\xa5\x77\x86\x67\xa6\xc9\xd4\x13\x19\x73\xad\x76\x89\x89\x27\xa9\x82\x42\xff\x43\x3c\xd3\x5c\x15\x10\x49\x6d\x72\x05\x4a\xf4\x6e\x35\x3a\xb3\x2d\xe7\x0a\x67\xca\x65\x20\x4e\xd3\xba\xee\x56\x38\xe2\xbe\x0a\x13\x8e\x78\xf1\x46\xe1\xd0\xab\xb1\x2d\xdb\x4f\xb2\xf5\x4a\xc9\xde\x07\x9c\x39\xeb\x22\xaf\x72\x0a\xb7\xe0\x5b\x6b\x27\x3c\x0e\xef\x70\xec\xc2\x57\xdc\x87\xdf\xd0\x3e\x80\x27\x38\xf6\xe0\x0d\xda\x9e\x38\x80\x1f\x48\xbf\x5f\x70\xe8\xc2\xbf\xf0\x18\xfe\xc0\xa1\x07\x7f\xa2\x07\xbf\xa3\xe7\xba\xf0\x17\xfe\x6c\x35\xff\x85\x58\xcf\x8a\x59\x95\x17\x3e\xb9\x9f\x8b\x22\xaf\xd7\x5b\x4d\xd0\x35\xc9\x1f\xc2\xdf\x83\x52\xc4\x79\x36\x9f\x15\x37\x6f\xfa\x46\x17\x92\x56\x09\xbd\xb9\x37\x77\x60\xdc\xeb\x52\xc3\x6f\x83\x9e\xc5\x66\x59\x5e\x2d\x45\x81\x19\xcc\x9c\xf7\xe7\x1f\xcf\x5e\x7f\xfe\xf8\x0e\xdd\xfe\xe5\xf5\xf9\x9f\x67\xe8\xf5\xaf\xaf\x8e\x4e\x4e\x71\xdc\xbf\x1e\x9f\x9e\x9f\xbf\xc7\xbd\xfe\xfd\x5f\x2f\x4f\x8f\x69\xfe\xfe\xdd\x16\x05\xe4\xe9\xdd\xb6\xa3\x3f\x8e\xce\xf0\xd9\xdd\x36\x05\xfd\xe0\x6e\x9b\x5e\xe2\x39\xcc\x9c\xa3\x8f\xaf\x4e\x4f\x5e\xe3\x21\xcc\x1c\x6d\x1b\xb0\x4f\xbd\x68\x15\xa8\xf4\x21\x09\x0b\xfe\xbc\x05\x89\xb3\x62\x51\xaf\x44\x56\x11\xe7\x49\x72\xaf\x12\x62\x35\x23\xbf\xfc\x22\xe2\x6a\x13\x35\x97\xd1\x16\x98\x96\x2c\xa5\xb3\x9c\x95\xe7\xd7\xd9\xbb\x22\x5f\x8b\xa2\xba\x61\x19\x8f\xb4\xca\x60\x02\xcb\x49\x36\xe5\x3e\x05\xc3\x03\xf7\xd6\x7f\x38\x59\x76\x69\x84\x6a\x33\x47\x4e\x2a\x72\x2e\xbb\x59\x7d\x7c\xcd\x32\x34\x5e\x1f\xbd\x3a\x79\xfb\xf2\xf4\xf3\xbb\xd3\x97\xaf\x8e\x2e\x0c\x4e\xfe\xa3\x00\x17\x8e\x60\x0c\x19\x29\x9f\x77\xe8\x36\x14\x0d\x4e\xb2\x29\xbe\x03\x35\x47\x11\xe8\xe4\xec\xcd\xe7\xb7\xe7\xaf\x8f\x36\x53\x9e\x77\x53\xbe\x6e\x4d\xf9\xaa\xa7\x1c\xfd\xf5\xee\xfc\xec\xe8\xec\xc3\xc9\xcb\xd3\xcf\x2f\x3f\xd0\x1c\xf2\x8e\x78\xf4\x2f\xe5\x0a\x81\x7d\x04\x6e\x3b\x9b\x5a\xbc\xe9\x36\x06\xbf\x11\x38\x1a\xf5\x44\x3d\x78\x53\xee\xd3\x82\xf6\xd1\xf6\x10\x9b\x51\x2f\x73\x43\x11\xd9\xc2\x17\x9c\xf3\x16\x81\xc9\x6f\xf0\x64\xda\xe2\xfd\xf2\xec\xcd\xd1\x63\x6b\xdb\xde\xdd\xc5\xbd\x0d\xe4\x37\xdd\xe2\x3f\x7e\xb9\xb8\xdb\x88\xe8\x0d\xda\xec\xc7\x5d\x04\x7c\x9d\x31\x83\xcc\x32\x06\xf1\x2c\x23\xcf\xe9\x52\x0c\x7e\x88\x22\x37\x40\x6c\xd0\x7b\x03\x3f\x5a\xf4\x8e\xde\xbf\x3f\x7f\xaf\x8e\x80\x09\x44\x1c\x0e\x45\xd3\x78\x88\x28\x9a\x86\xb4\x89\x88\x18\x29\x82\x7f\x21\xfb\x42\x7d\x3c\x3a\xf6\xf3\xad\x45\xae\x09\xa8\x86\xf9\x45\xc3\x7b\xf5\xfe\x3f\xdf\x7d\x38\xff\xef\xe0\xfd\x81\x43\x46\xad\xc3\x65\xd3\x74\xac\x39\xec\x58\x73\xc9\x41\x98\xe6\xf0\x0f\x95\x1f\xa0\x35\x8c\xb8\xb8\x59\x57\xf9\xa0\xce\x66\x57\x33\x99\xce\x2e\x53\x61\xc0\x92\x3f\x8e\xc3\x1f\x1a\x87\xb7\xe7\xaf\x3f\x9e\x9e\xdf\x63\x94\xc3\x8e\x72\x7f\x6e\x31\xca\x9f\x7a\xc2\xbb\xf3\x3f\x3f\xbf\x7b\x7f\xf4\xea\xe4\xe2\xe4\xfc\xec\x11\x76\xfc\x7d\x6b\xca\xef\x7a\xca\xf1\xf9\xfb\xb7\x2d\x4f\x3d\x90\x2f\x11\xfd\x85\x62\xfb\x24\x5a\x07\xb6\x1d\xb7\xc1\xf7\x2f\x28\x6e\x61\xe6\xac\x66\xdf\xf1\xa1\x50\x7d\x67\x1b\x71\xfe\xe0\xa4\x15\x57\x43\x65\xf6\x3f\x0e\x5d\xe8\xa1\xda\xef\x7b\xa0\x31\xf0\xd0\x75\x0f\xbc\xc3\xc3\xf1\xd3\xfd\x83\x7d\xf7\xf0\x70\x0c\x19\xbe\x9d\x55\xcb\x76\x3c\xe3\xbb\xc2\x1c\xbb\x87\x07\xde\x53\xef\x11\x35\xb1\x62\xf7\xc6\xf2\xc7\xf4\xc1\xf3\xbd\xe7\xcf\x9f\xb9\xcf\x77\x99\xe7\x1e\xec\x1d\xec\x7b\xcf\xc7\xfb\xbb\x77\xe6\x35\x2e\xb7\x58\x37\xea\x7e\xcf\x46\x57\x6c\xe5\x99\xef\x25\x8f\xd1\x85\x04\x27\x53\x48\x5b\x9b\xf4\x4d\x79\x73\xa2\x0d\x48\xc5\xe6\x04\xbd\x7d\x8a\x47\x85\xff\x0e\x72\x9c\x33\x41\x0e\xdb\x1f\x5c\x26\x6c\x69\x9a\x4b\x67\x21\xaa\xf7\x6a\xdd\x3f\x66\x69\x2d\x4a\x6d\xde\x2b\x7c\xd0\xa1\x02\xcc\x8f\x32\xab\xf6\xc6\x2f\x8b\x62\x76\xc3\xf2\x5d\x1c\x73\x1e\xe4\x61\x19\xf0\x1a\xbd\x3d\xcf\x3d\x18\xef\x56\x93\x72\x6a\xb1\x6a\x52\x5a\xde\x34\x0c\x43\xcf\xe3\x50\x87\x78\x28\xbc\xa7\x11\x2b\xfe\x01\xd0\x31\xe7\x40\x30\xb0\x20\xd1\xd7\x70\xb0\x50\xd2\xcf\x12\xed\x38\xd6\x3b\x9e\xf0\xf6\x39\x94\x16\x8e\x79\x50\x62\x3e\x1a\xf7\xc1\xa5\xda\x91\x26\xe3\x6f\x37\xd5\xf6\x6e\xb6\x1a\x09\xf3\x83\x1e\xf1\xf1\x73\x6f\xff\x60\xff\xf0\xe0\xd9\x81\xe7\x3e\x7b\xfa\x6c\x97\xed\x79\x26\x61\xc0\x2d\xcf\x3d\x3c\x7c\xea\x79\xcf\xc6\x07\x07\x07\xcf\x76\x35\x2e\xd6\xfe\xf8\x70\xff\xf0\xd9\xc1\xf8\x50\xb7\x8c\xa7\x96\xf7\xec\xe0\xe0\x60\xec\xe9\xf7\xbd\x76\xf7\xfb\xd3\x17\x2f\xbc\x67\x5c\xbf\x3c\x9d\xbe\x78\xf1\x9c\x5b\xf4\xf8\x6c\xda\xd3\xe3\x2e\x4e\x07\xdc\x89\xf3\xf5\x0d\xab\x28\xbc\x7f\x64\xab\x07\x7a\xab\x07\x7a\xab\x4a\xae\xbc\xfd\x5f\x69\x06\x95\x4e\x2a\xb5\xe7\xd6\x6e\x33\x63\x1c\x68\xd9\xb0\x36\x4d\x96\x4c\x4a\xcb\x9a\x62\x0b\x3e\xd0\x1e\x54\x32\xb1\xed\x72\x0a\x82\xbc\xea\xdc\x34\x05\x69\x6b\x7c\x3f\xb9\xb1\xc5\x14\x12\x3a\x92\x15\xcb\x47\x35\xdf\xad\xb9\xf2\xb1\xa8\x29\x48\xb4\x87\x05\xa5\x6d\x73\x9d\xb0\x2a\x79\x82\x13\xd9\x67\x25\x75\xf8\x61\x7b\xed\x14\x97\xa6\xe8\x9c\x0d\x07\x69\xe3\x8d\x5e\xbc\x54\xde\x64\x72\xdf\x9b\x54\xae\xe2\x4d\x48\x9e\x22\x8d\xb5\xcb\xde\x41\x4b\x1d\x81\x12\x52\x27\xc6\x04\xd2\xdb\x5b\xc6\xe1\xd5\xb6\x90\xf7\xd1\x92\xb8\x13\x7e\xde\x11\x9c\x2e\xfe\x27\xf1\xd9\x79\x09\x31\x66\xa3\x97\x8d\x4e\x1f\x08\xec\x13\xf0\x41\x62\xdb\x01\xcf\x51\x4c\x92\xe9\xce\x4b\xa8\xd5\x03\x0d\x84\x02\xe3\xdd\xdc\xaa\x77\x53\x90\x98\xee\xe6\x56\xb1\xf3\x72\xf7\xa5\x45\x5e\x07\x93\xa3\x4a\x09\x77\x41\x03\xb9\x15\xef\xd6\x40\xd3\x50\xee\x54\x9d\x58\x97\xa6\x29\xfa\xf4\x55\x79\x27\x64\xce\x1e\x44\x78\x2a\xcf\x34\xc4\x82\xe7\x58\x85\x45\xe4\xf9\xb6\xa7\xc3\x30\x4d\xdd\x1c\xdd\xa0\x0a\xa5\xca\x4f\x93\x02\x98\xc8\xe9\x10\xb3\x89\x9c\xf2\x9f\x84\xb8\x9c\x86\xf4\xa2\xa7\x69\xc7\xba\x45\x22\xdf\x2c\x5a\x6c\x16\xed\x12\x08\x92\xc0\xd2\xee\xc5\xa4\x9a\xda\x28\x41\x22\x3d\xbd\xc8\x26\x15\x01\x73\x81\xde\x50\xee\x16\x96\x1a\x40\xcd\x3a\xd8\x1b\x92\xd9\xa6\xfd\x75\xf7\x2a\x81\xe8\xce\x9c\x07\xb7\xf7\xf5\x5a\x1f\xc1\xea\xed\xa6\x9b\x24\x2f\x5c\xc3\x15\x5c\xc2\x39\x5c\xc0\x7b\x78\x09\x47\xf0\x1a\x3e\xc3\x77\x38\x46\xe9\x94\x88\xb9\x53\xaa\x2d\xc1\x09\x4a\x27\x86\x53\xcc\x9d\x58\xdf\xa3\x9d\x98\xe6\x89\xc2\xe0\xd4\x34\x4f\x29\xb0\xea\x22\x2b\xad\x26\xa5\x53\x9a\x66\x4e\x3f\xec\x24\x1a\x9e\x36\x0d\x0d\x1e\x22\x8d\xf4\x4f\x79\x74\x62\x9a\x2e\x22\xb5\x35\xcd\xf0\x34\x72\x77\x8f\xfd\xe3\x91\xeb\xbb\x23\x57\xf3\xea\x55\xab\x6d\x8f\x39\x5c\xe2\x95\xce\xb5\xc7\x28\x1d\x61\xe7\x8e\x80\x63\xac\xad\xd8\xf2\x20\x69\x1a\x96\xe0\x19\xc4\x58\x31\xe9\x90\xca\xb5\x2b\x96\xab\x07\x38\xc6\xe3\xd1\x4d\xe3\x72\x58\xa2\x1b\x9c\x4e\x96\x53\x44\x76\x32\x59\x4e\x29\x9e\x0b\x96\x6d\x50\x4e\xed\x61\xdf\x6c\x9a\xb1\x6d\x83\x1b\x1e\xf3\x4b\xad\x19\x3c\x0e\x0b\x1c\xba\x1b\x21\x3b\xc2\x93\x8e\xa1\x3f\xe3\x69\xf7\x48\x41\xe4\xb1\x85\x63\x58\x23\x85\x77\x8c\x36\x69\x79\x9c\xc3\x3a\xf4\x4c\x93\x9d\xa2\x60\xa7\xb0\x86\x84\xc3\x09\x0a\x76\xa2\x1f\xb7\xe6\x6f\xa0\x72\x78\x89\x9f\xe1\x1c\x4f\xfa\xab\x82\xcf\x1c\x2e\xf0\xbc\x0b\xbb\x3e\x87\x17\xc1\xf9\xe4\x82\xd4\x8a\xcb\x83\xef\x78\xda\x49\x10\x7c\xef\xf9\xdc\xe5\xf0\x5a\xd1\x19\x4e\x27\xde\x34\xc4\x64\x34\x36\xcd\xd7\x96\x15\xcc\xf3\xc1\x1a\x5d\x92\x44\x76\x0a\xe7\xf0\x19\x2e\x38\xb8\x61\x1a\xb1\xf7\x78\x4e\xc3\x3f\x0f\xf1\xc2\x34\xd9\x7b\x7c\xbf\x9b\x58\xec\x7c\xe2\x29\xa2\x70\xb5\xab\xf7\xa3\xd7\x6a\x3b\x11\x5b\x87\x2a\x29\xbd\xc6\xc4\xf6\x38\xcc\x37\x7b\xbb\xc6\x79\xb7\xa1\x0d\xc6\x6a\xb5\x39\x9c\xc3\x35\xad\xe6\x21\xa6\x34\xd7\xb6\xa1\x60\x73\xb8\x0e\x3f\x47\xdf\xfd\x53\xb8\x86\x84\x73\xee\x53\xe0\xbb\x36\x4d\x96\xe2\x1a\x15\xe8\x7e\x77\x77\x81\x87\xd7\xa6\x39\xdf\xde\x6e\xc1\xce\x61\x0e\x17\x84\x84\xdd\x2e\x71\x0f\x83\x7e\xbf\x5e\xa8\x10\xb0\x2c\x35\xe9\xa2\x45\xe0\x42\x21\xb0\x85\x36\xf7\x49\x93\x76\x43\xcf\x51\x65\x37\x2f\x27\x4b\x22\xfc\x1a\x52\xd3\x24\x82\x45\xed\x49\x9c\x4c\x5e\x12\xa5\x7c\x76\x8e\x13\x7a\x9e\xc2\x05\x7a\x3c\xb8\x5e\xca\x54\x30\xf6\xd2\xb2\x5e\x1c\x75\x49\x91\x73\x9d\x30\x3d\x26\x45\xbe\xc0\x4d\x1b\x5c\x2a\x49\xb8\xec\x24\x98\x82\xf2\x04\xf1\x4c\xeb\x89\x25\x7a\x70\x8c\x34\x24\x38\x56\x8a\xfb\x58\x29\x6e\xc5\xc4\x1f\xd9\x15\xd4\x16\xbb\x72\x04\x2e\xad\x58\xa5\x11\x2d\x0f\x4a\x58\xb4\x99\x64\xea\x88\xe1\xca\x29\xd0\x5a\x74\x6a\xf1\x4a\xe9\xf2\x87\x21\x1e\x8e\xfe\x66\x76\xc4\x5d\x36\xf9\x7e\x99\x4f\x39\xfb\x74\x3d\xf9\x74\xed\x4c\x77\x9f\xf0\x91\x84\x8c\x7a\x27\x7f\x3b\x53\x8b\x7f\x72\x9e\x8c\xa0\xc2\xd1\xdf\x9f\x9c\xb6\xe5\xc9\x08\x0a\x1c\xfd\x6d\x47\xec\x24\x4b\x64\x26\xab\x9b\xe6\x6c\x76\x46\xcd\x92\x86\x95\xbb\x9f\x2c\xa6\x60\xf1\xe6\xef\x4f\xa5\xd5\x7c\x2a\xad\x27\xa3\xc5\x03\xef\xeb\xbe\x8e\xc2\x32\xaa\xfd\xba\xbf\x3e\x92\x60\x3c\xf1\x0c\x25\xb8\x85\xbe\x14\x8d\x39\xcf\x9d\x12\x65\x79\x36\x3b\x63\xb1\x8e\x23\x7d\x37\x8c\x23\xdb\xf3\xbd\xfe\xca\x63\x48\x5a\x28\xc6\xb8\x07\x24\x60\xe3\xf0\x69\xcb\xd5\x59\x3c\x34\xbe\x1b\x88\xac\xc2\xea\xde\xb5\x56\xe4\x3d\xf3\x8d\x4b\xf2\xbc\xa3\xb1\xff\x1c\x12\xd3\x4c\x86\x98\x46\xc2\xcf\x6e\x39\xbd\xb1\x14\x13\xd8\x5e\x23\xd3\xc8\xf6\xef\x15\x18\xae\x43\x2d\x1c\xea\x21\xc6\xf7\xd4\x65\x0c\x29\x0f\xbe\xe8\x2b\x4a\x43\x39\xf1\x86\xc5\x92\xc8\x18\x5c\xce\x4a\x31\x30\xac\xc4\x37\x0c\x4e\xfe\x7d\x9b\xc7\xad\x39\xd0\xc6\x69\xbf\xb7\xb9\x13\x63\xde\x26\x5c\xe0\x2d\xba\xea\x74\x3f\x38\xb3\xcb\x32\x4f\xeb\x4a\x28\x1f\x10\xd5\xfb\xc3\x13\x6f\xef\xe1\x96\xb2\xbc\x7f\x0f\xc0\x84\x53\x92\x18\x8a\x5b\xf8\xe0\xc4\x42\xa6\x8f\x44\x03\xdd\x7d\x88\x9a\x0f\xf4\xab\x92\x68\x63\xae\xe6\xe4\xab\xf5\xac\x10\xf3\x0f\x39\x7e\x70\xe2\xd5\x1a\xb7\x69\xde\x83\x78\x8b\x1e\x48\x05\x60\xab\xb0\x42\xcd\x6f\xd3\x37\xef\x54\xde\x1e\x3f\x38\xf3\xf5\x63\x39\x89\x42\x95\x76\xb4\x46\xa9\xe8\x89\x5a\xa7\xa9\x76\xd3\x19\xcb\xb0\xe8\xee\x16\x3d\xb2\x0f\x1a\xcd\xd1\x0d\xe7\xbb\x37\x90\x21\x85\x47\xda\x87\xcb\x76\x3c\x17\xd1\x0d\x32\x25\x5d\x82\x64\xb4\x05\xe7\x86\x42\x45\x99\x6e\x4b\x8e\xb9\xbc\x92\x73\x31\xff\xed\x06\xd5\xf3\xaf\x76\xb6\x07\xaf\xee\xef\x0c\xde\xc1\x57\xbe\x05\x42\xa5\xdd\xc5\x42\x14\x1d\x2c\xd5\xf0\x2b\x80\xfb\x8f\x00\x74\xc1\x53\x00\xc5\xb7\x7a\x96\x12\x9d\xc4\xb7\x5f\x4d\x7f\x0a\xa4\xd5\x1e\xa7\x76\x92\xe6\x79\xf1\xcf\x8f\x78\x4f\x4d\x5a\x14\x62\x56\x89\xe2\xc3\x72\x96\x21\x45\x83\xbf\x5a\xf8\xd9\x23\x47\x1c\xba\xf7\x20\x9c\x17\x47\xb4\x05\xc5\x2e\x8b\x4a\xfc\x0a\xd6\x01\x59\x11\x64\xd9\x23\xfb\xe0\x3a\xf2\xcf\x08\xb0\x2c\x8f\x49\x0f\x89\x87\x5b\x1a\x0e\x35\xc7\xe8\x51\x2d\xf9\xb1\x7d\xfe\xf5\x70\xd3\xdc\x62\x9d\x50\xb7\x75\x7c\x35\xd6\xb0\xce\x66\x67\x8f\xcc\x57\x43\xcb\x76\x84\x58\xcc\x2a\x79\x25\xb0\x7d\x79\x84\xe0\x7a\xf8\x0b\x57\x4f\xf8\x2f\x51\xe4\xff\x1d\x4e\x2e\xb6\xfc\x3f\x71\xa7\x34\x23\x15\x65\xd9\x1e\x47\xfa\xcb\xe3\x78\xfe\xc8\x71\xe8\x05\xbb\xe9\xdb\x67\x91\xfe\xfa\x2c\x0e\x95\xbd\xfd\x9f\x0f\x43\xdd\x1c\xe1\x07\xa7\xac\x2f\xef\x81\xba\x1b\x31\x28\x18\x09\x96\x8e\xaa\xd5\x7a\xab\xc4\x10\xb7\x78\x3d\x53\xb5\x3c\xc3\xa4\x69\x86\xd9\x5d\xfd\xa9\x1c\x47\x32\x9a\xc3\x4d\x81\x15\x29\x30\x3b\x83\xd2\x59\xa7\x75\xc9\x04\x0f\x94\x55\x41\x75\x82\xa0\x72\xd4\xa3\x1b\x58\x62\xe9\xc4\xb0\x40\xd1\xaa\x90\xb4\x69\x86\xfa\xa2\x75\xb8\x6c\x9a\xe1\xa2\x03\xb6\x8c\x58\x0b\x4f\x70\x5f\xaf\xb9\x88\x4a\xbf\x5b\x77\xb8\xd4\xae\xec\x56\x75\xc1\x80\x9e\x1f\xce\xa2\x81\x51\xe9\xef\x21\x7e\x8d\x6c\xd7\x77\x95\xad\x4f\xb1\x62\x29\x57\x7e\xac\xba\x93\x5e\xf6\x7e\x5d\x82\xa9\x1d\x6b\x37\x80\xd5\xe8\x86\x09\x8f\x58\x82\x76\x02\x39\x2e\xb9\xcf\x62\x4c\x21\xc7\x05\x59\x83\x42\x5c\x89\x82\x6c\x15\x64\x98\xa8\x0b\xde\x7c\x73\x07\xb4\xd5\x7d\xbb\x15\xd4\xb0\x1a\x59\xd2\xdf\x5a\xf3\x17\x2c\xeb\xef\xf6\x39\x8f\x12\x3f\x83\x04\x33\x74\x03\x19\x66\x41\xa6\x03\x9f\xe5\x24\x9b\x0e\x71\x41\x5a\xf3\x67\x8d\xf4\xf6\x82\x5e\x36\x97\x09\x14\xfa\xe6\x48\x5e\xf1\x02\x16\x98\x83\x22\x80\x70\x4a\xc2\x8b\xc9\x0d\x7c\x5b\xa5\x2a\x3a\xbf\xb7\xbb\xa9\xd6\x37\xd3\x93\xa2\x75\x71\xa9\x29\xc3\x33\xdb\x0b\x64\x98\xe8\xeb\x91\xa5\xba\x62\x7d\xb1\x50\xa1\x97\x2e\xb4\x92\x41\x61\x9a\x43\xea\x28\xa6\x34\x79\x8a\x19\x0f\x6c\x9b\x9e\x60\x39\x91\x53\x0b\xcf\x6e\xe9\xdf\x46\x9a\xa5\xee\x32\x28\x54\xa6\xa3\x08\x96\x7d\xa4\x6c\xdb\x71\xaf\xf1\xd5\x29\x9d\x30\x01\x4b\x88\xb9\xaf\x0e\x51\x9f\x98\xe7\x7b\xb0\x75\x99\x01\x42\x29\xc2\x55\x3e\xaf\x53\x12\x96\x55\x3e\x7f\x84\xc3\xf5\xad\xb9\xaa\x41\xdc\x98\x3d\xef\x2e\x6f\x0f\xa5\x13\x37\xcd\x50\x38\x65\xd3\x08\x12\xed\xa1\x2e\x5c\x88\x36\x0c\xee\x53\x53\xd3\x48\xd5\x2b\xb7\x7b\x25\xf7\xd9\x21\xe2\x9f\x11\x2b\x94\x88\x28\xdb\x0d\x15\xbe\x62\x12\x04\xb8\xb0\xc7\x55\x53\x01\x95\x53\xee\x62\xc1\xfd\x4d\xd7\x9f\x1c\xa4\x16\x50\x56\x39\xea\xa2\x96\x09\xae\x6d\x42\x46\xda\x4a\xcc\x51\x3d\xfd\x53\xdf\x41\x9d\xb5\xf6\x77\xb5\xb1\x24\xe9\x23\xf7\x63\xfe\x18\x65\x3a\xba\x40\x4e\xf1\x66\x2b\xf5\xe3\x47\xa5\x3e\xff\xb5\xd4\xe7\x0f\xa5\xbe\xdb\x53\x2b\xf6\x35\xaa\xf8\x50\x57\x81\x8c\x6e\x20\x51\xe1\x6c\xda\x8b\x7d\xdd\x34\xc3\x52\x8b\x3d\x69\x97\xf4\xee\x3a\x79\x27\xe5\x89\x96\xf2\x74\x4b\xca\xe9\x99\xdc\x40\x35\x90\xfa\x23\xe9\xbb\xbb\xb9\x12\xeb\x1a\x2b\x56\x73\x52\x6c\xac\x24\x51\x4e\x7a\xb1\xce\xb1\xb6\xdb\xbc\x59\x1e\xba\x11\x2b\xb1\x86\x02\x53\xee\xb3\x1c\xed\x1c\x0a\x4c\x38\x14\x1b\x99\x0d\x72\xdb\x0e\x8a\x8d\x38\x6f\x75\xb5\x37\x73\x49\x17\xee\x64\x98\x76\x8f\x6e\x98\xdb\x99\xaa\xbb\x4b\x81\xdc\xd3\x04\x0b\xc8\x30\xa7\xd5\xdd\x20\x0b\x78\x8e\x2c\x99\xd8\x76\x36\xc5\x64\x92\x4d\xad\x94\x7e\x72\x3e\x3a\x6b\x5c\xa0\x86\x1d\x3c\xeb\xce\x35\x37\x4d\x96\xf4\x21\x57\xce\xc1\xb2\x4a\x0e\x24\x1f\x09\x94\x8a\x57\xfa\x3a\x00\x52\xf3\xdb\x27\xad\xcf\x59\x65\x3d\xf4\x49\x4b\x2c\x34\xd1\xfb\x0c\xaa\x18\xaa\xf4\xbd\x69\x7a\x43\xa4\x77\x57\xff\x31\x9d\x7f\xdb\x03\xa3\xcb\x39\x1b\x2a\x05\x0f\x62\xa8\x87\xb7\x59\x58\x4e\xc2\x73\xdf\xf3\xab\x50\xf6\x5e\x1f\x64\x58\xed\xde\x58\x24\x10\x72\x52\xb5\x5a\x23\xa8\x5a\x77\xaf\x52\xee\x5e\x46\xee\x9e\x4e\x63\x4a\x52\x0b\x95\x0a\xb4\xda\x3e\x0a\xb4\xfa\x5b\x4b\xd3\x2c\xc8\x05\x0a\x89\xb2\xe4\x5b\x0a\xcb\xe3\xa0\xcc\x9c\x2a\x7b\x78\x4c\xfc\x1f\x11\x15\xa6\x2b\x91\x44\xd3\xf4\xf9\xe3\xa7\x9c\x9b\xe6\x47\x56\xc1\xbf\xff\x2d\xac\xde\xd3\xba\x53\x60\xec\xc2\x73\xf0\x9e\xea\xca\xa7\xcc\xff\xca\xa1\xa2\x75\xd5\xa9\x3c\x24\xf9\x1d\x85\xa3\x6e\x75\x2e\xe0\x02\xbc\x67\x5b\xf4\xe4\x51\xd6\xca\xbc\xe1\x09\xc3\x52\xb5\x33\x2d\x2b\x67\xa4\x65\x32\xa5\x64\x4c\x93\xd9\x17\xba\x68\xe6\x82\x66\x94\xbb\xea\x1e\xc8\xf5\x3d\x52\x4a\x99\x3a\xff\xf2\x5b\x3d\x2b\xc4\xfb\x3c\xaf\x88\x01\xbe\x15\xd5\x63\xce\xfa\x03\x3b\x4f\x22\x58\x3a\x25\x45\x7a\xaa\x90\xea\x9d\xb5\x0f\x8b\x96\x5a\x86\xeb\x3c\xd5\xc1\x1e\xb1\x05\xd9\x65\x92\xcc\x64\x4b\xf4\xf4\x38\x32\xd9\xae\x0a\xeb\x69\x80\xea\x8f\xdc\x91\xeb\x27\x51\xa9\x10\x0c\x94\x7d\x55\xa9\x7f\xc2\x8b\x11\xe7\xba\x0a\x60\x8a\xe8\x8d\xdc\x88\x4e\x91\x25\x1c\x58\x57\xc6\x63\xc5\x7c\x67\x8c\xaa\x8a\x31\xd3\x35\x52\xb0\x0d\x20\xd3\x86\x9a\xc5\x96\xc7\x47\x63\x6e\x33\x37\x8c\x9b\x26\xde\x19\xd3\x30\x05\x31\x43\x4d\x4e\x9f\x91\x34\xde\x29\x75\x51\xe6\x39\xdb\xd4\x64\x6f\x2a\x2c\x85\xc1\x2d\x8f\x5b\x31\x07\xd9\x52\x20\xe3\xdc\xef\x9e\x53\xcb\x30\x48\x53\xd3\x79\x28\x43\xa9\xb2\x61\x90\x62\x6c\x2d\x61\x4f\x6d\x3f\x25\x83\x19\xe8\xfa\x57\x09\x64\x69\xf5\xd1\xd6\xda\x01\x7a\xc5\x4a\xa8\x61\x09\x9e\xba\x9c\x63\xb5\x13\xf3\x1e\x8d\x94\x6b\x37\xae\x60\xd2\x89\xf9\x76\xbb\xd2\x89\xd2\x11\x2f\x62\xd3\xb4\xed\x74\x0b\xf9\xd4\xde\x83\x94\x78\xdf\x38\x3c\x3c\x3c\x34\x14\x8f\xb2\xbc\x69\x8c\xfd\xf6\x95\xf3\x9f\x6c\x68\x65\x4d\x33\xb4\xb2\xbe\x10\xd9\x34\x8d\xa7\x06\x62\xd6\x55\x06\xba\xc4\xf4\xec\x23\x93\x20\x1d\x61\xbd\xb3\xc6\x40\x31\x27\x0e\x65\x8b\xbc\xe4\x8e\xf8\xc6\xca\xed\x6a\x85\x61\xae\x66\xd4\x50\xb7\x33\x5c\x0e\x75\xb7\xd7\x6e\x38\xff\x29\xb1\x6e\xe7\x2c\x2d\xdc\x87\x94\x7e\x72\xf4\x6e\xfb\xc0\xa6\x5b\xd2\x83\xaf\xad\x19\x57\x30\xc8\x8a\xd7\xe9\x7f\xe7\xa7\xb6\x75\x40\x5d\x02\x75\xa5\x53\xa8\x9a\xab\xcf\xf1\xd2\x89\xe1\x02\xc9\x8e\x1d\xdc\xb1\x63\xbc\xcb\x9c\x9e\x9b\xe6\x85\xce\x20\x99\xe6\xc5\x56\xe6\x74\x78\x49\x86\x53\x7b\x00\xe7\xa6\x39\xd4\x23\x86\x17\x4d\x73\x41\x7f\xfa\xed\xbc\xaf\xaf\x10\x6d\xfc\xaf\xbc\x93\x5d\xbc\x74\x4a\x20\xc8\x91\xae\xb5\x70\x75\xfd\x8a\xcb\xfd\xed\x7a\x0c\x0e\xa2\x2d\x49\xab\xd8\xa5\x8a\x64\xac\x8a\x09\x9d\x30\xed\xa1\xa4\x9b\xdc\xd9\x02\x2f\xfa\x47\xc5\x63\x2b\x3c\x87\x73\xbc\x80\x0b\x5c\x41\xae\xcc\x8a\x72\xf2\xc8\xa4\xa4\xd6\x02\x56\x38\x99\x2a\x5b\xb5\xda\x2a\x3f\xca\x0b\x76\x8d\x67\x70\x85\x2f\xc9\x55\x0d\x6c\x3b\x0f\xd1\x0d\x36\x45\xf2\x6b\xbc\x98\xe4\xd3\x9d\x2b\x98\xab\x87\xd1\x55\xe3\x42\x89\x29\xd4\x98\x5b\x65\x50\x87\x79\xc0\x63\x3c\x57\xf7\x26\x3b\x57\xb0\xc4\xf3\x49\xa9\x07\x25\x38\xdf\x8d\xad\xe5\xee\x1a\x62\x5c\xef\xc6\x56\xb2\x73\xb5\x7b\x65\xad\x26\xf5\xd4\x2a\xa0\x40\x16\x8f\xae\xd5\x0d\x41\x42\xa3\xb9\x35\xdf\x5d\xc2\x6a\x52\xdb\xf6\x14\xe3\x9d\xeb\x80\xc6\x61\xd1\xb1\x43\x11\x59\x96\xf4\x57\xbd\x33\x48\xb6\x6d\x05\x52\xb3\x45\x5b\xb6\xf6\x0f\x55\xfb\xe0\xde\xe5\xa0\x47\xca\xfd\xf9\x76\xa9\x9c\xbe\x28\x54\x2e\x52\x86\x0f\x15\xfc\xf3\x5e\xc1\x83\x88\xc8\x20\xd0\x72\x7e\xa5\x51\xd9\xd2\x25\x8f\x87\x65\x9f\xdb\x50\xec\xc1\xfd\xe4\x21\x8f\xc8\xb2\x78\xed\xc2\xd4\xa0\x41\xaa\xca\xbb\xff\x1d\xb0\xb1\xab\x81\x75\x66\xaa\x83\x39\x76\x3b\x98\xaa\x86\xef\x51\x8a\xfd\x12\xa6\xf7\x0b\x98\x9e\xd2\xe1\x3a\xce\xdd\x72\x1b\x9d\x72\x9d\xca\x4a\x97\xa6\xe7\x68\xfd\xe5\xf4\x85\x3c\x50\xd3\xeb\xc3\x5a\x1e\x28\xb1\x1b\xd5\x55\xf1\x90\x27\x48\xc2\x92\xa2\x9c\xa8\x12\xed\x2e\xfe\x86\x19\xc6\x51\xd2\xeb\x2d\x3f\x81\xe5\xa6\xfc\xa9\x0d\x73\x0a\xcc\xc9\x93\x83\x1a\x0b\x58\xda\x58\x70\xc8\x43\xd7\x34\x97\xa1\xdb\x71\xf7\x72\x27\x6f\x9a\x1c\x12\x9c\xb5\xdf\x44\x30\x17\x0a\x1e\x2c\xc3\x22\x28\x2c\xcc\x79\x62\x61\x69\xf5\x7d\x05\xe4\x3c\xa8\x43\x55\x3e\xdf\x76\xa8\xe5\x0b\xce\x21\x56\x35\xf5\x86\x6d\x58\x09\xbf\xad\x30\x8d\x12\xeb\x2f\xe7\x7e\x89\x93\x45\x41\xa2\xf5\x97\xf3\xa0\x2c\x89\x47\xe9\x26\x33\xb9\xf5\xa5\xd0\xa7\x4f\xf3\x9f\x86\x55\x5b\xc6\xed\xa7\x4f\xbf\x19\x60\x2c\x0c\x0e\xc6\x13\xd3\x78\x00\xa3\x5b\x81\xfb\x29\xf7\x93\x4d\x61\xae\x3e\xec\x76\xe8\xa3\x6e\xdf\x3d\xa5\x89\x5f\x60\xa1\x55\xe5\x1a\x17\x4e\x0c\xf3\xfe\x5e\x1d\x56\x58\x6d\x5e\xae\x31\xb9\x73\xe3\xde\xb3\x0b\xfb\x82\x43\x0f\x4a\xec\x4b\xb1\xbf\xe0\x12\xd8\x90\x51\x24\xaf\x72\x38\x8c\xf3\xa6\x29\x9d\xb4\x62\xdf\x94\x71\xd1\xe5\x11\x63\x30\x56\xb3\xef\x83\xb9\xc8\xf2\x95\xcc\x68\x2b\x03\xc3\x62\xcb\xc8\xb8\x57\x03\xfc\x58\x09\xb0\xc0\xe1\xd2\x34\x55\xc2\xe5\x23\x2b\x41\x3b\x66\x1e\x77\x16\x95\x60\xdf\x78\x54\xfa\x9d\x1b\xba\xee\x63\xff\xed\x32\x74\x6d\xae\x0b\xb6\x26\x3e\x9d\x3b\x02\xfb\xc4\xd1\xc2\x11\xb6\x07\x73\x65\xd5\xf1\xfd\x84\xd5\x98\xef\xdc\xf0\x17\x6e\x74\x63\xd5\x7e\x3d\xa5\x85\x05\xed\x25\x5e\xad\xd9\x9c\x87\x6e\x44\xc1\xc2\xdc\x5f\xf9\x25\xd4\xf8\x03\x7e\x90\xb7\xd1\x93\x22\xe6\x90\x68\x48\x6e\x90\x22\x99\xfb\xb9\xca\x0e\x2a\x59\x51\x2e\x40\xda\x5a\xc9\x6b\xce\xc1\x1b\x52\x08\xb4\x5a\x53\x84\xc4\x2b\xbc\x86\x6b\x94\xb0\xc2\xe4\xee\x48\x89\x2b\x4e\x91\x8b\x84\x39\x96\x6d\x48\xb5\xe9\x9b\x73\x0a\x6e\x64\xa7\xf7\x24\xbe\x62\xa2\x8b\x25\x39\x5c\xeb\xd5\x93\x0e\x66\x67\xd2\x09\x62\xd5\xa1\x24\xb7\x50\x4a\x9c\x12\x57\x4e\x89\x0b\xa7\x84\x7c\x17\xc7\x90\xe1\x2b\x46\xd6\x35\x87\xaf\xbc\x85\xbb\xe0\xce\xec\xb2\x64\x5c\xa1\xfe\x8a\x25\x50\x3d\xd6\xcb\x5f\x78\xd1\x64\xb5\x75\x06\x70\xbd\xf5\x32\xf5\x27\xc9\x76\x5f\xb5\xdd\x07\x3f\xb0\xd6\xee\x7c\x95\xeb\x1a\xe1\x87\x91\xef\x96\x63\x6d\x89\xa6\x21\x03\x1c\xb9\xbb\xc2\xd1\xf9\x20\x3d\xf7\x5d\x7e\xad\xd2\x8a\xeb\xfc\xfa\x17\xd1\xd0\xaa\xab\xa6\xb2\x04\xef\xd2\x03\xe4\x20\xf4\xae\xfa\x78\x0f\x0c\xd1\xaa\x7b\x55\xff\x33\xec\xb2\x9a\x4c\xf0\xa6\x29\xc2\x0b\x8a\x81\x46\xe8\xf2\xa6\x59\xcf\x8a\x52\x1c\xa7\xf9\xac\x62\x82\x2b\x39\x19\x32\x81\x84\xce\xbd\x9b\x06\xe5\xc7\xae\xf3\x6b\x66\x49\x10\xbc\xcb\xb0\xfc\x1e\xcd\xd9\xef\xa3\x1b\x6b\xcc\x7d\x17\x36\x52\xd8\x56\xa4\x16\x3b\x63\xf5\xaf\xae\x45\x5a\xb7\x0c\x86\x95\x13\xb7\x95\xa2\x99\x69\x56\x7d\x36\x54\x05\x46\x9b\x57\xcc\xb8\x2e\x0f\x5e\xb1\x62\x34\xe6\xd0\x15\x2d\x07\x12\x37\x3e\x1e\x64\xa6\xa9\xd2\x1a\xf2\x2e\x18\x79\x07\xcc\x9d\x6c\x7c\x85\xdf\x9c\xb9\xbc\x62\x15\xe7\x90\x29\x2b\xf9\x3b\x7c\xed\xad\x64\x5f\x24\xfe\xcf\xcd\x9a\xaa\x8a\xdb\xff\x95\x99\x86\xf1\x7e\x7b\xb0\x9a\x53\x1e\x3b\xd3\x2e\xbe\xad\x88\x7f\xb1\x72\x44\xb0\x15\x94\x22\x62\x1e\x49\x0a\x36\x8c\xee\x8e\xcc\x00\x37\x94\x2a\x8a\x24\xb5\x4e\xde\x7e\x86\xc6\xd9\xec\xcc\xf0\x95\x2b\x4e\xf4\xed\xfd\x83\x16\x49\xf5\x85\xe9\xf8\x69\xf7\x89\x69\xf4\x9a\xa5\x2c\x83\x9c\x83\xdb\x08\xf0\x5c\x90\xdc\xff\x2d\x44\xb2\x39\x21\x3e\x89\x12\xd5\xe7\x77\x43\x68\xb1\xaa\x8b\xe8\xfa\x45\x5b\x66\x2f\xea\x2c\x6e\xb3\x3d\xea\xf9\x9f\xdf\x05\xe8\xfb\x87\xab\x59\x5a\x8b\xf3\x84\xa6\xe7\xbf\x5f\x9c\x3f\x92\x09\xd7\xa9\xed\x8d\xa8\xdd\x6e\xe8\xdf\x55\x9d\x92\x3a\x9f\x6d\xea\x25\xaa\x4d\x2c\xeb\x36\x3d\x35\x45\xe8\x36\x8d\x40\xc4\x2c\xca\xfc\xcc\xf6\xee\xd4\x57\x6c\x2a\x2b\xb4\x90\x79\x20\x37\x45\x28\xb9\xfa\x4e\xc5\x32\x8c\x40\x86\x45\xeb\x81\x66\x28\x54\xb6\xd1\x32\x0c\xa8\xf0\xc6\xee\xbf\xe5\xa8\x6c\x3b\xc8\x28\xfa\xb3\x32\x1e\xe4\x16\x66\xb7\x6d\x21\xc8\x9d\xaf\x12\xf3\xbb\x5f\x25\x4a\x1e\xf4\x6e\x60\xbe\xf9\xde\xcf\xf2\x9a\xc6\xe3\x1b\x44\xe5\xfd\xdc\xa0\x70\x62\xc8\x29\x2a\x52\xdf\x14\x95\xa4\xd3\x9d\x52\xd5\xcf\x50\x8c\x97\x39\x62\x2b\x4b\xf5\x30\xd3\x61\x9a\x43\xe5\xc4\x14\x98\x9b\xe6\x30\x57\x45\x5d\x4d\xd3\xdf\x86\x55\x51\x11\xb9\xbe\x5d\xfa\xb5\x72\x5c\x86\xd8\xc3\xa8\x35\x00\x37\xac\xa1\xc0\x04\x31\x85\xa1\x6c\x9a\x61\xce\x7b\xaf\xd8\xf5\x87\xf2\xef\x4a\x97\xb5\xdc\xb9\x62\x4b\xc2\xb4\x6b\xd7\xc5\x45\x2c\xe9\x53\x2e\xfc\x05\x4b\x7b\x3a\xf1\x28\xf1\xc9\x99\x77\x83\x32\xac\x83\x5a\x67\x91\xe5\xa4\x9e\x0e\x31\x9f\xd4\x7d\x30\x4f\x2d\x21\x35\x74\x50\xfb\xcf\xa4\x31\x8d\x5c\x7f\xb3\xdc\x86\x8a\xf9\xdd\xdb\x5b\x26\xf4\xc7\x3f\x21\x39\xd3\x55\x88\x5b\xd5\x3e\x35\x31\x46\xfb\xd1\xdf\x44\x17\x47\x0e\x54\x29\xdc\xd4\x40\x3c\x57\xef\x9b\xf2\xf3\x9e\x45\xf5\xf7\x48\x62\xeb\xdc\xca\x07\xdf\xff\x90\x7b\xa3\xa2\xad\x5a\x15\xca\xf7\xdf\x3b\xd1\x5e\xdb\x6f\x40\x37\x1c\x23\x6d\x3b\xc8\x27\x72\xba\x8b\x59\x5b\x0f\x36\x29\xd0\x9d\x5a\x78\xde\xa7\x01\x44\x17\x18\x13\xa1\x78\x50\xbc\xe8\x27\x17\x96\xc5\xf3\x49\x31\x0d\x2b\xf5\xb5\xae\xd6\x29\xf9\xa4\xb0\x3c\x12\x67\xfd\x80\x2e\x07\xfd\x64\x51\xd7\x74\x54\x35\x2e\x35\x4c\x77\xb0\xea\xf5\xe7\xf6\x5d\x40\xbf\xb3\x64\x5b\x3f\xb2\x4d\x55\x51\x24\x36\x91\xba\x65\x38\x86\x25\x36\x2e\xb1\xe0\x16\x73\xc3\x2c\x32\xc8\x6f\x12\x96\xc1\xad\x6c\x03\x30\xbd\xc3\xe2\xba\x6c\x2d\xeb\xdc\x62\xc3\x75\x8c\xc0\xb2\x32\x72\x82\xd5\x37\x68\x02\x0b\x4b\xf4\x05\x86\xd5\x46\x64\x2d\x2b\x0b\xab\xcd\x34\x03\x32\x1b\xab\xc0\xb6\xb7\xa6\x5a\x58\xe8\x99\x95\xb2\x19\x9b\xba\x32\xfd\xc9\xfb\x16\xce\x19\xdf\xc4\x68\x1b\x4c\xe3\x0d\x73\x0c\x04\xde\x31\xa4\x40\x16\x7a\xce\x04\xf7\x57\xc4\x07\xb4\x99\x99\x8e\xfb\x75\xb5\xc3\xa7\xb9\xc5\x3e\x39\x9f\xe6\xbb\x3c\x6a\xe8\xdf\xe2\x4c\x4c\x2c\x7b\x1a\xd1\x63\xf4\x64\x44\x6e\x93\x32\xb8\xb1\x90\x29\xac\xf4\xb3\xba\x6a\x85\x6b\x6c\xab\x75\x07\x97\x79\x9e\x8a\x59\x36\xc8\x8b\xc1\xa5\xcc\x66\xc5\xcd\x60\x4e\xe1\xa6\x01\x57\xa8\xbf\xa4\x92\xd9\x62\xb0\xca\xe7\xc2\x80\xcb\xee\xc3\xf4\x01\x31\xea\x60\x39\x2b\x07\xab\xbc\x10\x83\x6a\x39\xcb\x06\xde\xd3\x41\x29\x17\x99\x4c\x64\x3c\xcb\x2a\x0d\xa4\x34\xe0\x1c\x0d\xd7\x1b\xef\xed\x3f\x7d\x76\xf0\xfc\x70\x76\x19\xcf\x45\xb2\x58\xca\x2f\x5f\xd3\x55\x96\xaf\xbf\x15\x65\x55\x5f\x5d\x7f\xbf\xf9\xf1\xf2\xb7\x57\xaf\x8f\x8e\xdf\xfc\xeb\xe4\xf7\xff\x7b\xfa\xf6\xec\xfc\xdd\xff\x7b\x7f\xf1\xe1\xe3\x1f\x7f\xfe\xf5\x9f\xff\xf5\xe4\xb3\x01\x67\xe8\x09\x6f\x1f\x6e\xd0\xdb\x87\x8b\xfb\x85\xbd\x1e\xbc\xc7\x89\x47\xe6\xc7\x73\x5d\xf0\xc4\x1e\x78\x62\x1f\x3c\xf1\x14\x3c\xf1\x0c\x3c\x71\x00\x9e\x78\x0e\x9e\x38\x04\x4f\xd0\x20\xe1\x79\xf4\x33\xa6\x9f\xbd\x29\xbc\x54\x1f\x72\x1c\xa1\x27\x0e\xd5\x17\x55\xaa\x8a\xd2\xe8\x8e\x67\x53\xec\x3c\x17\x89\xcc\x84\x69\xea\x7f\x67\xb6\x9a\x73\xfd\xc8\x1e\x9a\x9a\xd9\xed\xe6\xbb\x4d\xa3\xce\xf4\xb8\xf9\xa6\xfa\x5b\x5d\xd8\x08\xd3\xd4\xff\x0e\x79\x59\x45\xa5\x2f\x00\xee\x36\xe1\x0c\x86\x4b\x5e\x15\x37\x3f\x97\x58\x88\x6f\xb5\x2c\x04\x6b\xeb\x41\x0d\x7e\x1b\xcf\xaa\x78\xc9\x5e\xf3\x9f\xb7\x9a\x03\x85\xd3\x7f\x59\x86\xb3\xdb\x36\x2b\xf0\x1f\xff\xf1\xff\x03\x00\x00\xff\xff\x62\x5b\xe2\x13\x79\x43\x00\x00")
func bignumberJsBytes() ([]byte, error) {
return bindataRead(
@@ -86,11 +86,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, digest: [32]uint8{0xe3, 0xcc, 0x96, 0x2a, 0x17, 0x4b, 0xc8, 0x5f, 0xc7, 0xa9, 0xe5, 0xd, 0x1d, 0xea, 0xf3, 0xca, 0x34, 0xef, 0xa4, 0x2, 0xa2, 0xa0, 0x55, 0x3d, 0xf2, 0xcd, 0x92, 0x4a, 0xfa, 0x2, 0xc3, 0xa9}}
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\x73\x60\x18\xc6\x99\x0c\x1b\x02\x33\x64\x6f\x20\x5c\x40\x76\x76\xef\x6c\x96\xab\xed\x96\xed\x1e\xda\xdd\xfe\x75\xb7\x73\x18\x92\xef\xfe\xbf\x54\x3a\x95\x0e\x7d\x70\x12\xe6\xb4\xc9\x0b\x70\x4b\xa5\x53\xa9\x54\x2a\x95\x4a\x55\x19\xfd\x7f\xcb\x28\xa3\x7b\x9d\xc9\x32\x19\x17\x51\x9a\x10\xda\x29\x7a\x49\x2f\xeb\x7e\x51\x29\x79\x27\xed\x2d\xbb\x5f\xa2\x49\x67\x3d\x39\x49\x4f\xf9\xaf\x02\x7e\x9d\x05\x19\x09\xf6\x8a\xcb\x05\x4d\x27\x44\xd6\xb5\xd7\x92\x45\x5b\x0f\x1e\x88\xc4\x5d\x56\x66\xf9\xe0\x41\xd0\xcd\x68\xb1\xcc\x12\x12\x74\xd2\xde\xfa\x66\x97\xa5\x47\x32\x2d\x12\x69\xac\xd6\xc9\x5e\x42\xcf\xc9\xcb\x2c\x4b\xb3\x4e\xeb\x20\x48\x92\xb4\x20\x93\x28\x09\xc9\x3c\x0d\x97\x31\x25\xed\xd6\x46\xba\xd1\x6a\xb7\xba\xbb\xc5\x2c\x4b\xcf\xc9\xa4\x3f\x4e\x43\xba\xd7\x7a\x73\xf4\xe2\xf8\xf5\xcb\x4f\x6f\x8f\x3e\x7e\xfa\xf1\xe8\xf8\xed\x8b\x56\x6f\x72\xcd\xea\x8b\xf7\x58\xdf\xf7\xbe\xd0\x8b\x45\x9a\x15\xf9\xf0\xcb\xf5\xf5\x2e\x1b\xc3\xc9\xe6\x69\x7f\x1c\xc4\x71\x27\xee\x8b\xac\x9e\xec\x7d\x87\xf2\x01\x26\x7b\x00\xb8\x75\x7a\x42\x4f\x77\x45\x57\xf3\x4e\xf2\x2c\x19\xd2\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\xdb\xdc\x4d\xbf\xcf\xfa\x31\x4d\xa6\xc5\x6c\x37\xdd\xd8\xe8\xe6\x9d\x8c\x21\x5e\x75\xe3\xba\xdb\xf9\xb2\x35\x3c\x51\x5d\x16\x55\xf4\x38\x96\x7a\xa2\xed\xee\x97\x35\x9e\x20\x3b\xb3\x77\xb2\x46\xc8\x97\x35\x42\x08\x69\x8d\xd3\x24\x2f\x82\xa4\x68\x0d\x49\x91\x2d\x69\x8f\xa7\x46\xc9\x62\x59\xe4\xad\x21\x39\x81\x6f\x09\x0d\x79\x49\x30\xa7\xad\x21\x69\x7d\x4a\xcf\x13\x9a\xb5\x7a\x3a\x87\x8d\x8e\xe5\x04\x61\x98\xd1\x3c\x6f\x89\x9c\x6b\xf8\xff\x54\x54\x2d\x8b\xc3\xff\x22\x2d\x5d\x16\xf5\xed\xa5\x9f\x50\x11\xa3\xbd\xd1\x65\x41\xf3\x9d\x6d\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\xd1\x7b\x3a\x8d\xf2\x22\x0b\xfe\x0b\x26\xaf\x57\x55\x07\x3d\x3f\xba\x15\xdf\x2f\xb2\x20\xc9\x27\x5e\xd6\xf7\x67\xc1\x41\x66\x91\xc2\xea\x48\xc8\x69\xf1\xa1\x9a\xa4\xee\x0c\x17\x76\xd3\xbf\x49\xa3\x5f\x79\x02\x82\x26\x88\xaf\xaa\x60\x91\x45\xf3\x20\xbb\xf4\xf6\x23\x4d\xe3\xda\xc9\xdb\x17\x6d\xfd\x79\x51\x68\xee\xc1\x95\xd5\x94\x21\xe1\xa0\x74\x1b\xff\x23\x21\xc1\xdb\xfb\x30\xca\xd3\xf3\xe4\x16\x3d\x0f\x92\x34\xb9\x9c\xa7\xcb\x7c\x85\xae\x47\x49\x48\x2f\x68\x68\xec\x5d\x77\x36\xb1\xba\x72\xd4\x1d\xb3\xf6\xf3\x28\xb9\x0d\xe3\xde\x5f\x02\x26\x5e\x26\x21\x0d\x5b\x16\x9a\xe8\x19\x23\x84\xbf\x00\x8e\x46\x51\x18\x36\xc3\xd1\xcd\xea\x3f\x0b\xe2\xa5\xb7\xfb\xcb\x28\x29\xb6\xbf\x79\x52\x3d\x05\x6f\xe9\xf9\xf3\xe8\x77\x44\xfe\xad\xd6\xdc\xc1\x2c\x48\xa6\xbf\x27\xe9\xdc\x09\xe5\x94\xd4\x8d\xa4\xfa\x4a\xaa\xf1\x62\xe6\x1d\xdf\x8d\x6a\x11\xb4\x76\xba\xb6\x76\xdd\xfb\x72\x7d\xda\xdb\xfe\xdd\x0e\xfd\x7f\xa1\x33\xef\xef\x24\x3b\x4e\x96\x49\x78\x63\x52\xb9\xf5\xc6\x75\x7f\xec\xfd\x73\x1f\x7b\xef\x0f\x7d\x7f\xe4\x33\x87\x77\xf0\xe2\xbc\xf0\x47\x93\x36\xbf\xee\x66\xae\xf7\xaa\x9d\x3b\xdb\xab\x56\x9d\xf7\x49\x96\xce\x6f\x39\xed\x45\x7a\xcb\xa3\xe6\xed\x04\xbe\xdf\x77\xdd\xfc\x11\xf0\x17\x25\x61\x94\xd1\x71\x71\xe8\xdd\x33\x57\xe8\xc9\xed\x26\x22\x1a\x07\x8b\x8f\xbf\xeb\x64\xf8\x31\xd9\xec\xb4\x4b\x17\x69\x1e\x55\x1d\xd4\x17\xc1\x65\x30\x8a\xa9\x29\x14\xfc\x2e\x5c\xa9\x8c\xe6\xee\xe4\xf8\x75\x3b\x1a\xd8\x97\xe3\x7d\x61\xe2\xf3\xb7\x3f\xc9\xdc\x09\x92\x4a\xea\x6e\x46\x67\xbf\x03\xfa\xff\xb0\x58\xbf\x8b\xf3\xe3\x8d\xf9\xe4\xd7\xc6\xba\xcd\xf4\xee\xd1\xde\x10\xed\xb7\xde\xb8\xbe\xf6\xcc\x1e\x7a\xb6\xb4\x2a\x39\xee\x71\x13\x39\x0e\x8c\x37\xc8\x9e\xb4\x70\xe8\xb4\xfb\x83\x49\x9a\xcd\x83\xa2\xa0\x59\xde\xee\xee\x02\xc0\x87\x34\x8e\xc2\xa8\xb8\xfc\x78\xb9\xa0\x26\x2c\x6b\x9f\x41\xad\x0d\x1e\x3e\x5c\x23\x0f\x0d\x48\xa1\x73\x27\x51\x4e\x02\xb2\xc8\xd2\x94\x01\x93\x62\x16\x14\x24\xa3\x0b\x76\xc8\x4a\x8a\x9c\x88\xb9\x23\x2c\x93\xd5\x70\x58\x90\x79\x50\x8c\x67\x34\x1f\xb2\x4f\x91\x8d\x7e\x9e\x9c\xe2\x8f\xc7\xc6\xd7\xa9\x99\xb9\x63\x7d\x9f\x9e\x3c\x39\x3d\x39\xed\x91\x7e\xbf\xbf\x46\x1e\x0e\x9c\xb1\xc9\x1e\xef\x11\x65\x4d\xd3\xe9\x8a\x29\x2e\x66\x51\xde\xff\x04\x0b\xe3\x47\x89\x20\x06\xd8\xe7\xe8\x3a\x64\x19\x87\x49\xb1\x8b\x80\xf9\xbe\xed\x83\x3e\x82\x1c\xd1\xdc\xee\xda\xf5\xee\xda\x9a\xa7\x1f\xfd\x45\x96\x16\x1c\x6b\x7b\x24\xa1\xe7\x46\x5f\x3b\x5f\xae\xbb\xbb\xd5\xa5\xfa\x20\xbd\x64\xcb\x71\x91\xb2\xc6\x3d\xb0\x75\xed\xf6\xa3\x5c\xcc\xb9\x46\x08\x23\x47\x89\x14\x61\xd7\xb2\xbe\xce\x12\xfb\x30\x6f\x9d\x81\xc0\x76\xe7\xdf\x27\x9d\x93\xcd\x47\xdf\x9d\x3e\xec\xfe\xfb\xb4\xfb\x6c\xd0\xe5\xe3\x34\x0f\x0e\xa5\xdd\xba\xee\x7d\x69\x61\x52\x6c\x0d\xbf\xeb\xb5\x38\xbd\xb5\x86\x5b\x8f\xaf\x4f\x7b\xdf\xfc\xce\xe4\xfd\x3c\x4d\xe3\x1a\xda\x1e\x31\x90\x12\xc2\x66\x79\xf2\x7f\x4e\xa5\xf0\xeb\xb1\xfe\x79\x8a\x92\x77\xf0\x47\x1d\x19\x43\xcf\x6e\x4a\xc3\xac\xf0\x2a\x44\xcc\xe1\x6d\x0a\x66\xa9\x2b\x92\xaf\x59\xa4\x82\x76\x79\x8b\x55\x65\x6f\x42\xb5\xff\x61\xa8\x35\x69\xf6\xe1\xff\x34\x22\x5a\xd1\x9f\x7a\x8a\x7d\xf2\x7b\x53\x2c\xdb\xc3\x14\xc9\x16\x7e\x9a\x2d\x66\x94\xc0\x66\x07\x84\xdb\xf7\x51\x2e\xcb\x55\x3f\x04\x5d\xc2\xcf\xc7\xe8\xf7\x29\xce\xd8\x31\xbe\x4c\xfa\x25\x62\x6b\x55\x3f\x9f\x1a\xf5\x88\xa2\x1e\x2a\x87\x4e\xde\x98\xcc\x59\xe9\x95\xe8\x9c\x17\x70\x08\x9d\x25\xaf\x4a\xe9\x66\x99\x2a\x52\xe7\x8d\x56\x96\xbe\x19\xb1\xb3\x4a\x38\xa9\x7f\xd9\xea\x5d\x77\x6f\x46\xf8\xa2\x77\xf5\x94\xff\x6d\x13\xca\x1f\x3c\x84\x0e\x7f\x9c\x45\x39\x99\x44\x31\x65\x94\xba\x08\xb2\x82\xa4\x13\x72\x4e\x47\x3b\xfd\x5f\xf2\xfe\x1a\x80\x88\x2f\x06\x30\xc9\x28\x25\x79\x3a\x29\xce\x83\x8c\x0e\xc9\x65\xba\x24\xe3\x20\x21\x19\x0d\xa3\xbc\xc8\xa2\xd1\xb2\xa0\x24\x2a\x48\x90\x84\x83\x34\x23\xf3\x34\x8c\x26\x97\x50\x47\x54\x90\x65\x12\xd2\x0c\x08\xbe\xa0\xd9\x3c\x67\xed\xb0\x8f\x9f\xde\x1e\x93\xd7\x34\xcf\x69\x46\x7e\xa2\x09\xcd\x82\x98\xbc\x5b\x8e\xe2\x68\x4c\x5e\x47\x63\x9a\xe4\x94\x04\x39\x59\xb0\x94\x7c\x46\x43\x32\xba\x14\x54\x44\xc9\x8f\xac\x33\x1f\x44\x67\xc8\x8f\xe9\x32\x09\x03\x36\xe6\x1e\xa1\x51\x31\xa3\x19\x39\xa3\x59\xce\x66\x68\x47\xb6\x25\x6a\xec\x91\x34\x83\x5a\x3a\x41\xc1\xc6\x90\x91\x74\xc1\x0a\x76\x49\x90\x5c\x92\x38\x28\x74\x59\x17\x05\x7a\xa4\x21\x89\x12\xa8\x76\x96\xca\x95\x1d\x15\xe4\x3c\x8a\x63\x32\xa2\x64\x99\xd3\xc9\x32\xe6\x82\xe3\x68\x59\x90\x9f\x0f\x3f\xbe\x3a\x3a\xfe\x48\xf6\xdf\xfe\x8b\xfc\xbc\xff\xfe\xfd\xfe\xdb\x8f\xff\xda\x25\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\x3f\x78\xb5\xff\xf6\xe3\xfe\xf3\xc3\xd7\x87\x1f\xff\x45\xd2\x8c\xfc\x78\xf8\xf1\xed\xcb\x0f\x1f\xc8\x8f\x47\xef\xc9\x3e\x79\xb7\xff\xfe\xe3\xe1\xc1\xf1\xeb\xfd\xf7\xe4\xdd\xf1\xfb\x77\x47\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\x27\x24\x49\x8b\x1e\xc9\x29\x25\xdf\xcf\x8a\x62\x31\x1c\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\xed\xcd\xad\x6f\x80\xe1\xd5\x6f\x05\x15\x02\x2c\x2a\x23\xe4\x31\xdf\xde\x21\x24\x05\x04\xcc\x76\x41\x1f\xe4\x61\x52\x98\x80\x51\x52\xf8\xe0\x8e\x1d\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\x76\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\x3f\xcb\x82\x4b\x0e\xce\x99\xb8\x25\x0a\x1c\x30\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\x07\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\x64\xf3\x74\x97\xf3\xcf\x68\x42\x3a\xeb\xb8\x11\x5c\x07\x7f\xa0\xc1\x9f\x72\xb4\xa3\xe4\x2c\x88\xa3\x50\xd3\x00\xab\x71\x7d\x48\xda\x64\x83\xe0\xca\xd7\xb0\xac\x81\x6b\x36\x29\xb0\x84\xd2\xc8\x22\x0e\xa2\x84\xd3\x97\x35\x8d\x1c\xe0\x9d\xc8\x29\x9f\x45\x91\x7e\x34\xfa\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\x61\xc0\xa7\x3d\x72\x02\xe0\xa7\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\x27\x50\xe4\x94\xb3\x67\xf9\xa5\x56\x11\xea\x8f\xd8\xa7\x8e\x26\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\x67\xab\x4b\x06\x64\x67\xbb\x4b\x1e\x92\x9d\x6d\x31\x68\x84\xbe\x60\x3c\x26\x1b\xa4\xa3\x36\x1d\x03\xeb\x25\x28\x24\xcf\xd0\xde\x45\xc8\xce\x36\x19\x1a\x09\x25\x9d\x95\xa8\xef\x91\x4d\x8c\xfd\x8c\xe6\xcb\xb8\x90\xd4\xc3\x67\xf0\xcd\x32\x2e\xa2\x9f\xa3\x62\xc6\xe7\x44\x52\xa0\xd1\xb7\x9e\xa2\xa3\x9e\x39\x83\xb2\x72\x31\x42\x5e\xbf\x79\xe2\xf3\x93\xbe\xd5\xaa\x6f\x0d\x34\xec\x01\x5a\x23\x6a\x78\xad\xd6\xae\x5e\x38\x34\x9e\x88\x11\x8b\xce\x8a\x5d\x21\xcd\x5e\x06\xe3\x59\xc7\x66\x4c\x11\xa6\x2d\xc6\xf5\x4b\xe7\x4b\xcf\xd5\x69\x17\x17\xe2\x08\x81\xae\x6c\xb8\xda\xce\x8e\xd9\x7d\xb9\x8e\x10\x11\xaa\xb5\xcb\xa8\x98\xc6\x13\x01\x62\xcf\x11\x74\xc0\xed\x92\xc4\x13\x7c\xd8\x93\x85\x9b\x30\x97\xe2\xc6\x1e\xa1\xe2\x19\x1e\x19\x90\x6d\x0d\x7a\x4d\x68\x9c\x53\x6b\x78\x83\x01\x09\xd3\xa4\x5d\x90\x20\x0c\x89\x28\x55\xa4\x66\x95\x7d\x12\x15\xed\x9c\x04\x71\x46\x83\xf0\x92\x8c\xd3\x65\x52\xd0\xb0\x04\x4b\x5f\x69\x9c\xd7\x7a\x11\x0e\x06\xe4\xe3\xd1\x8b\xa3\x21\x99\x44\xd3\x65\x46\x09\x3b\xb0\x25\x34\x67\x27\x40\x76\x4a\xbb\xcc\x4d\x66\xf5\x5b\x10\xc9\x1f\x67\x92\xcd\xc9\xa0\x18\x81\x12\x2b\x25\xcb\x5c\xa1\x35\xa3\x93\x00\xd4\x31\xe7\xb3\x34\xa6\xbc\x87\x51\x32\x5d\xaf\x61\x04\x15\x3c\xc0\xe6\xfc\x62\xd0\x3d\x92\x3a\x2b\xdf\x58\xe4\x72\x4e\x6a\x45\x7d\xcf\x16\xd7\x71\x55\x63\x88\x80\x78\xc3\xe4\x3c\xd0\x64\x9d\xd3\xc2\x99\x53\x4e\x56\x6f\x83\x39\xb5\xf7\x21\x9d\x83\xe5\x4c\xb7\xac\x67\xf3\xa9\xde\xcf\x74\xc5\x9e\x3a\x15\x5f\x14\x18\xd4\x52\xad\xfc\xab\x18\xb6\xac\x64\x91\xd1\xb3\x28\x5d\xe6\xaa\x43\xdb\xbb\x0c\x25\x51\x42\xa2\xa4\x70\x4a\xd4\xe1\x1f\xf5\xd7\xd7\x20\xfb\x9b\xa4\x19\x81\x47\xc2\x11\xd9\x23\x5b\xbb\x24\x22\xdf\xcb\x01\xc8\xf7\xc2\x24\xda\xd8\x28\x2b\xce\xfe\xac\x3e\x6f\xec\x91\x8d\x8e\xc4\x41\x44\x1e\x91\xad\x53\x26\xe1\x93\xab\x2b\xb2\xb9\x5b\x5a\x49\x05\x2b\x17\xf4\xb0\x41\x22\xf2\xb0\x6c\xe6\x36\xec\x5e\x30\xe1\xa0\x8c\xed\xcb\xbf\x6b\x27\xd5\x4c\xb9\xee\x76\xba\xd6\x14\x0e\x06\x64\x12\x65\x79\x41\x68\x4c\xe7\x34\x29\xd8\xf9\x8a\xa3\xa9\x47\xf2\xcf\xd1\x82\x44\xc5\x2a\x53\x6e\x60\x7f\xd3\x87\x7d\x86\xbf\xca\x19\x80\xa7\xf3\x61\x18\xb1\x46\x82\x58\x2d\x72\x81\x4f\x87\xff\xb8\xf8\xf6\xf3\x45\x4d\x3a\x25\x0c\xe2\x24\x22\x1b\x64\xeb\x54\xf2\x09\xb2\x41\x9c\x6e\x78\xd0\x5e\x8b\x60\x8b\xf9\x79\x20\xc5\x56\xe9\xa1\x7d\x4e\x15\x37\x66\x3d\x7f\x68\xa6\xc2\x84\x2d\x13\x53\xb7\x5c\xfc\x35\x94\x49\xca\x18\xd2\x66\x15\x43\x22\x8d\x68\xba\x96\xa3\x0c\x06\x64\x1c\xc4\xe3\x65\x1c\x14\x54\x0a\x3e\xec\xc8\x27\xfa\x42\xa2\x82\xce\x6f\xc1\x8e\x18\x2b\x3a\xf9\x13\x31\xa5\xae\x0d\x7b\xbd\xd2\xbe\x72\xcb\x09\xf9\xfd\x18\x0c\x66\x2e\x5f\x9d\xb7\x10\x47\x5b\x24\xfa\x51\xa3\x0d\x11\xba\x48\x71\x33\x99\x56\x68\x8c\x38\x64\x63\x8d\x91\x4c\x57\xb7\x9a\x4a\x25\xe2\xd7\x25\x95\xeb\x41\x50\xc3\x1e\xf1\x0f\xea\xf7\xe9\x88\x50\x31\xad\x23\xe2\xd0\x20\xdb\x34\x41\x4b\xa5\x92\xa8\x04\x21\x65\x3a\xa2\x72\x84\x88\x12\x70\xc2\x80\xd6\x34\x62\xaa\x35\x44\x78\x88\xbe\xd3\xb1\x81\x9b\xd5\x15\x44\xb2\x14\xa7\x62\x0c\xcf\x89\x38\xf7\x9e\xc2\xad\xe3\xfe\x1d\x6b\x94\xf8\x90\x3b\x30\x32\xb9\xbe\xb4\x5a\xc4\xd0\x8b\xc8\x1a\xb5\x86\xa9\x4a\xe5\xa0\x47\x55\xab\x67\xc0\x18\xe5\x1c\x88\x95\xb9\xeb\x91\x36\x51\x47\xa9\x93\xa8\x4f\x0e\x16\x5d\x2b\x65\x92\x83\x01\xc9\x97\x73\x7e\x43\xe7\xd9\xa5\x84\x88\xa8\xe0\x45\x75\x27\xd1\x29\xe3\x8a\xea\x0b\xb6\x24\x1f\xff\x91\xcd\x9b\x88\x90\xd2\xa6\x83\x82\xc1\x80\x64\x74\x9e\x9e\xc1\x35\x26\x19\x2f\xb3\x8c\xc9\xa7\x4a\x38\x4d\x21\x59\x74\x33\xca\xa1\xe7\x9e\xde\xe6\xab\x68\xfc\x24\x32\x1b\x6b\xfe\x8c\x91\x91\x47\x4e\xfd\x8d\x29\xed\x83\xb5\x0e\x4b\xae\x75\xbc\xa7\x56\xc9\xe3\x3c\x54\x56\x58\x57\x0e\x92\xac\xd8\x0e\x86\x2f\x49\xcc\xfb\x0b\xde\x5b\xd6\xd6\x58\xdc\x32\x61\x53\x0b\xe8\x7d\x87\xdb\xab\xda\x26\x18\xe2\x5a\xb4\xd3\xed\x79\xb3\x9f\xa7\x69\x5c\x96\xc7\x84\x90\x92\xac\xe3\x8a\x3c\x7c\xb9\x59\xda\x6c\x55\x26\xe7\xc2\x65\xb9\xef\x69\x50\xda\xe3\x63\x9e\xb9\xc6\x08\xc2\xb5\xdf\x00\xd4\x29\x9b\x0d\x69\x38\x3b\x7c\xdc\x6b\xf1\xbb\xdf\xd6\xf0\x1b\xf8\xc9\xfa\xd6\x1a\x3e\x61\xbf\xf1\x75\x6c\x6b\xf8\xb4\xe7\xb3\xf5\x88\x92\xa2\x35\xdc\xda\x64\x3f\x33\x1a\xc4\xad\xe1\xd6\x36\xfb\xcd\x6f\x65\x5b\xc3\xad\x1d\xf6\xb5\xe4\x50\xd0\xc0\x52\x80\x3d\xb9\x3e\xed\x3d\xfd\x2d\xed\xa2\x6a\xae\xa1\x6f\x66\x4d\x84\x2b\x59\xc5\xa8\xc8\x2c\x67\xdb\x16\xe1\xdc\x15\x4d\x8c\xfc\x45\x2b\x2c\x8d\xcc\x9e\x34\xa9\xeb\x16\x76\x47\x25\xc6\x46\x8d\x1a\x45\x57\xe2\xde\xe9\x92\x6c\x27\x5b\xd2\x06\x26\x4c\xd6\xb0\xeb\x2d\x99\xbe\xbb\xb7\x64\xba\xb7\x64\xfa\x6f\xb1\x64\xd2\x0b\xe1\xae\xcc\x99\x9e\x47\xd3\xb7\xcb\xf9\x08\x58\xa1\xe2\xce\xa3\x68\x9a\x40\x62\xff\x17\xc5\xc9\x97\x45\x14\x9b\xf6\x35\xfd\x01\xa4\xf1\x7f\x25\xd8\xd8\x0b\x32\x4e\x93\x49\xe4\x18\x03\xc9\x93\x19\xda\x15\xe0\xec\x02\xdb\x82\x1c\x38\xe7\xd5\x39\x01\x7e\x4f\xe0\xc1\x06\x3b\x67\x31\xbe\xa5\xad\x64\x61\x29\xb0\xb9\x01\xe5\xcc\x43\x86\x63\x0e\x19\xe5\x24\xa1\xd3\xa0\x88\xce\x68\x4f\x72\x22\xb8\x38\x2a\xce\xd3\x76\x4e\xc6\xe9\x7c\x21\xa5\x55\x28\xc5\xe6\x56\x95\x9c\xc4\x69\x50\x44\xc9\x94\x2c\xd2\x28\x29\x7a\xfc\x3a\x94\x91\x7d\x98\x9e\x27\xd6\x99\xce\x54\x93\xb8\xc7\xb7\x2b\x8e\xe5\x2b\x85\xef\x6b\x39\x16\xb6\x94\x12\x4a\x43\x38\x45\x8f\xf4\x1c\x87\x7e\x63\x18\x40\xda\xb5\xb2\xf3\x31\xdb\x35\x18\x30\xd4\x2f\xb9\xb0\x6a\xb7\xcf\xe7\xa2\x33\xee\xbf\xfc\xf8\xea\xd3\xf3\xc3\x9f\xde\x1e\xbf\x79\xfe\xf2\xfd\xa7\xf7\x47\xc7\x6f\x5f\x1c\xbe\xfd\xe9\xd3\x9b\xa3\x17\x2f\xd1\x19\x4e\x69\xe2\x60\x26\xfb\x8b\x20\x7c\x4d\x27\x45\x87\x7f\x15\xe9\xc7\xf3\x34\x3f\x50\x58\x14\x6d\xf6\x8b\x54\x88\x4b\x5b\x4f\xba\x3d\xf2\xe4\xb1\x79\xc3\x83\x77\x4b\x18\x4e\x87\x37\x62\x1a\x60\x98\x13\x2f\x0f\xbf\x25\x38\x7f\xae\xce\xc6\xe6\xa1\x79\x55\x1c\xba\x52\x87\x81\x45\x0f\x42\x8a\xf4\x15\xbd\x90\xe3\xce\x97\xa3\xbc\xc8\x3a\xdb\x08\x7f\xb1\x75\xb5\xcf\x8b\x4b\x2d\xf7\x06\x79\xb2\xd3\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\xb8\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\xde\xbc\xdd\x5f\x9b\x6c\x88\xea\xc9\x33\x42\xda\x5b\x6d\x32\x24\xed\xcd\xf6\xed\x79\x54\x1d\x26\xd9\x89\x95\x95\xfa\x07\x83\xcb\x09\x13\x8c\xe7\xcb\xb8\x88\xb8\x50\x39\xba\x24\xdb\xff\x99\x33\xf1\x5c\xd9\xd0\x05\xac\xe6\x82\x4e\x69\x56\xb1\x95\xbc\x17\xb5\xd6\xed\xdf\xab\xce\x88\xb0\x65\x2e\x99\x11\x81\x26\x8b\xfa\x18\xd6\x54\x8b\x6a\x73\x8d\xe6\x34\xb7\xb2\xb6\xbb\xfd\x45\x7a\xde\xd9\xda\x7e\xda\xed\x9a\x28\x3d\x98\xd1\xf1\x67\x12\x4d\x0c\x9c\x22\xb1\xc8\x42\x44\x1e\x4d\x13\x1a\x1e\xe6\x6f\x75\xb6\xa3\x88\x56\x75\xcc\xe8\x85\xe8\xb1\x89\x0c\x49\xb4\x70\xe8\x83\xb6\x0b\x53\x12\x4b\xd9\x91\xe5\x3c\x62\x62\x78\x10\xe7\xda\x6a\xd9\x6e\xbd\x16\x5f\x3e\x0c\x49\x76\xb3\xd9\x23\x5b\xdd\x1e\xd9\x7a\x82\xe4\x91\xed\xae\x91\xdb\x25\x7b\x7b\x7b\x8c\x64\xbd\x54\x98\x31\xf6\xf1\x28\x88\xa1\x53\x84\xab\x0e\xf4\x85\x07\x17\x35\x5d\x22\xe2\x8a\x04\x5b\x08\x34\xc8\xc3\xb1\x83\x65\x38\xd3\x82\x61\x45\xbb\x4a\x38\x84\x65\x11\x4d\x09\x97\xd3\x2d\x7a\x53\x5d\x30\xf0\x67\x18\xc5\x32\x60\x3e\x8f\x7b\xbc\x37\x48\x97\xd9\xe9\x92\xab\x2b\xd2\xda\x6c\x09\x1d\xf1\x60\x40\xc6\x8a\x8a\x98\xf0\x2c\x27\x52\xb5\xce\x81\x60\x96\x95\x98\xed\x4a\xd8\xf2\xf2\xd6\x9a\x64\x31\xb1\x1e\xfd\xa3\x67\x72\xf9\x7c\xce\xa3\x64\x69\x2f\x81\xf6\xe4\x96\x7f\x6d\xa8\x5b\x56\xbe\xa5\xee\xc6\x1a\x74\xe8\x06\xe4\xb3\xac\xa6\x9f\xe3\x4a\x02\xf2\x91\x0e\x5d\x89\x76\x44\xf3\x2e\xd5\x1c\xdf\x05\xd9\x7c\x1d\x94\x09\x7e\x5f\x86\x32\x87\x71\xd7\xa2\x0c\x30\x86\xe4\x61\x13\x45\xa2\x39\x17\x45\x0e\x27\xf7\x99\x9b\x5b\x2b\x51\xc0\xf4\xc3\xe8\x2c\x0a\x69\xf8\xfc\xb2\x82\x81\xdf\x84\x9a\x6a\x70\x73\x7c\xd7\xc8\x59\x96\x62\xe7\x78\x65\xf4\x1c\xdf\x06\x3f\xee\x15\x2c\xaf\x5a\xa1\xa8\x4c\xdc\xd2\xaf\xa5\x1b\xe3\x45\x6e\x6b\xe6\x5c\x94\xe2\x48\x34\xed\xa2\xc8\x11\xce\x7c\x18\xf2\x2c\x2f\xd8\xac\x6e\x29\xad\x6d\xb5\xc9\x33\xbe\x2f\x0b\xb7\x18\xab\x61\xb3\xf4\xd8\x88\x1e\xe5\x56\x6c\x7c\x31\x9d\x68\xc4\x31\xf1\xa1\xe2\x60\xe3\xc8\x1d\x49\x30\xa7\xfc\x75\x0f\xfb\x65\xc9\x5f\x02\x86\xd5\xa9\x6a\xf0\x60\xde\x39\x81\x42\x1b\x3d\x82\x35\xe5\xac\x90\x78\x5f\x4d\xf6\x48\xd9\x33\xdd\x87\xdd\x01\x3a\xcf\xe4\xd1\xaf\x82\x27\xe6\x70\x45\x25\xca\x9f\x6c\x9d\x9a\x72\x70\x7b\xf3\x82\xc9\xcb\xee\xe4\xf6\xf3\x38\x1a\x53\x26\x96\x6c\x93\x87\x50\xdd\x8a\x74\x5e\x33\x33\xf8\x08\x7e\x67\x13\xb4\x2a\xfa\x4b\xf5\x00\xce\x26\xa3\xce\x87\x16\x1f\xe0\x88\x13\x37\x60\x36\xe6\x9e\x3c\xee\x8a\x3d\xbc\x48\x05\x7c\x97\x3c\x94\x47\x4a\xdf\x0c\x58\x15\x71\xd1\xf0\xc9\xe3\x9e\x68\x7f\xb5\x29\xa8\x38\x92\xf3\xe1\x7b\xce\xe4\x77\x8a\xfd\x20\x1f\x47\x51\x15\xfe\x3d\x67\xf9\xdf\x10\xf3\x52\xa5\x03\xaa\x81\x66\xf8\x5f\x6d\x02\xb4\x6f\x9a\xb2\x19\xd8\xd7\xde\x6b\x4a\xa6\xa0\x94\xb7\x97\xa0\x5c\x55\xe8\x62\xdb\xe7\xbd\x66\x05\x69\xca\xc0\x5d\x6b\xf3\xa2\x45\x36\x88\x38\xe0\x00\xda\xf9\x6f\x65\x53\xf0\x78\xb3\x47\x70\x52\x99\xc3\x80\x2f\xd2\xee\x03\x1d\x34\x87\xd6\x77\xcf\x86\x81\x15\x3b\x74\x52\x1c\x38\xbc\xc0\x87\x65\x19\x4e\x29\x8e\xcc\xa1\x9b\xe4\xf6\x23\x4d\xe3\xa1\x9d\xe0\x40\x31\x09\x64\x68\x27\x60\x28\x25\x96\x0d\xed\x04\x17\xea\xd8\x01\x3b\xf6\xc2\xe1\x46\x75\x8a\xa7\x3e\x17\xf0\xd8\x0f\x89\x07\xab\x53\x3c\x70\x18\xdb\x28\xc9\x85\xf4\x4d\x8f\x9b\xe3\x96\x33\x27\x08\xa7\xb9\xb0\x82\xea\x87\xde\x75\x77\x2d\xef\x74\xcd\x9b\xa1\xd6\x70\xeb\x69\xaf\x65\xde\x28\xb5\x86\xdb\x60\xbe\x00\x0b\xa3\x35\xdc\xda\xea\xb5\xf0\xbd\x54\x6b\x68\x7e\x5e\x9f\xf6\xb6\x36\x7f\x67\x7f\x2e\x87\xdc\x30\xbe\xc2\x01\x51\x94\x14\x65\xfe\x87\xc4\xd5\x55\x94\x14\xdc\x35\x0b\xfb\xf1\x58\xfd\x3a\xd5\x89\x3b\xe8\xb7\xe5\xb9\x25\x4a\x0a\xee\xb7\x25\x4a\x8a\x27\x8f\x15\xd8\x53\x5d\xd1\xf6\x37\x4f\x4a\xea\x62\xf0\x35\x7e\x8c\xec\xa3\xe1\x57\x74\xc5\x05\xe0\xb6\x0d\xc2\x61\x52\xac\x68\x76\x61\x94\xa8\xb0\xb6\x80\xe6\x2a\x4a\xde\xc8\xb6\x22\x4a\x0a\x29\x2a\x3e\xbb\x91\x3f\x17\xde\xab\x7a\x1b\x88\xad\x46\x21\xec\xee\x8d\x20\xee\x8d\x20\xfe\xbc\x46\x10\x44\x5b\x41\x70\x51\xe9\x8e\x0c\x20\x1a\xd8\x35\xd8\xac\x9e\xdb\x2d\xa4\x60\x8d\xae\xdd\x76\xf4\x3d\x12\xea\xf9\x8c\x26\xea\xb1\x62\x8f\x1b\x7e\x33\x01\x5c\x79\x6f\x90\x92\xe5\xc0\x6b\x18\x61\xe9\xbe\xed\xb7\x89\xc0\x49\xa5\xfc\xc8\xff\xbf\xba\x22\xed\x36\xe2\xb3\xa9\x7c\xb6\xc0\x7f\xec\xa2\x77\x86\x51\x22\x5a\x6f\xec\xee\x63\x4a\x0b\x6c\xef\x0b\xd6\xe3\xed\x5c\xbe\x02\x05\x5e\xc2\x2a\x31\x4c\xdd\xb5\x7c\xcf\x2d\x5d\x4d\x29\x5a\xaa\x99\x74\xad\xb8\x32\xd2\x91\x7d\xec\x1a\xd6\xec\x80\x1e\x6c\xcd\x6e\x37\x52\x69\x87\x06\x26\xfe\xc6\xb1\x03\xdf\x3d\x36\x46\xc6\x38\xa3\x8c\x98\xe4\x7a\x30\x7d\xb2\x70\x72\x0f\xa3\xc9\x84\x82\x35\x32\x47\xb9\x75\x2e\x39\x57\x8f\x42\xf0\x71\x44\xa2\x44\xcc\x92\x34\x5c\x4e\xbc\x87\x10\xf3\xe8\xc2\xb6\x43\x5f\x3f\x82\x05\xe7\x30\xaa\x17\xe5\xa8\x3c\xf7\x3f\x98\x35\xe9\xae\xf4\x4a\x4f\x13\xa4\x22\xd5\x55\x30\x9a\xce\x47\x51\xe2\xba\xb7\x29\xd2\x29\x65\xdc\x9d\xd5\x40\xa7\x7d\xbe\xa8\x82\xc5\x82\x26\xb0\x96\x82\x84\x3f\x80\xb0\xb0\x2b\x6a\xab\xbb\x84\x11\x8c\x69\x16\x8d\x19\x7b\x92\xbd\xaa\x2f\x2c\x6e\x4f\xd3\x89\x80\x85\x7d\xa8\x12\xb5\x72\x78\x75\x7a\xbf\x2a\xb4\x2a\xbd\x05\xbf\x32\xd9\x25\xf5\xd8\x1d\x07\x71\x2c\xf0\x2b\xef\x70\xf8\x88\x66\x81\x5e\xba\x79\xf4\xab\xf0\x2c\x08\x77\x75\xb3\x20\xef\xb1\xff\x25\xa1\x81\xef\x5f\xcf\xa5\x1d\xc6\xb7\x32\x04\xf5\xeb\x4c\x2b\x51\xe3\x77\xcd\xe4\x5b\xb8\x62\x55\xac\xef\xed\x81\x74\x31\x89\x12\xeb\xa1\x52\x1d\x12\xb4\xcb\x22\x51\x95\xb8\x5e\xb6\x95\x06\x3c\x77\x3f\x7f\x5e\x7e\xf4\xe7\x1a\x5f\x57\x43\xd3\x60\x99\x19\xb5\x57\x0d\x7a\x1d\x46\xad\xdf\xff\x77\xc9\x33\xd2\x6e\x93\x61\x33\x6b\x2c\x84\x32\xaf\x4d\xd6\x0a\x78\x63\xbc\x9f\x2b\x27\x94\xcc\xe8\x7b\xeb\xa5\xf5\x17\x7e\x9c\xc9\xbd\x47\x5e\x09\x07\x98\xe1\x07\x73\x4c\x64\x40\xe2\x95\x58\xd4\x8d\x79\x51\x08\x4e\x95\x6c\xfc\xf9\x9c\x33\xa9\xe5\xb5\x4b\xf8\x95\x1f\x29\xa1\x3b\x31\x61\x9d\xd5\x51\x67\x6c\x6b\x25\xb8\x43\x9b\x92\x1f\x79\x32\x21\x90\x37\xf0\x0d\xb0\x48\xe7\x8b\xe2\x12\xab\x04\x1b\x6c\xa2\xb5\xab\xd0\xa4\x47\xc4\x9e\x86\x20\x7d\xac\x80\x1b\xe9\x6e\xaa\xd4\xd1\x94\x17\x13\x95\x03\x11\x55\xd6\x8d\xc1\xb8\x58\xd9\xf0\x88\x05\x37\x19\x87\x7e\x89\x57\xee\x1c\xea\x75\x94\x17\xce\xb3\xbf\x13\x63\x34\xa7\x1e\x8f\x50\x95\xa3\xd7\x35\xbb\xdb\x8b\x7a\x14\x24\xaf\xe9\x97\x8b\x90\x9b\xb5\x8a\x47\x70\x4a\x15\x59\xa4\x05\x7a\xe8\xca\x0b\x4b\xe1\x88\x3b\x1d\x22\xc6\xc3\x3e\xf5\x7e\x50\x80\x9a\x6f\x8a\x8c\xbd\x4d\xad\x47\xbe\x7d\x95\x2c\x48\xfb\xf6\xcb\xf6\x14\x62\x36\x4f\xf6\x70\x8f\x35\x2c\x1e\xc6\xc6\x9e\xab\xe8\x17\x4f\xb5\xdc\xe7\x59\x1c\x52\x8b\x40\x9d\x14\x3f\xb9\x55\x4f\xe6\x06\x03\x39\xdd\xf4\x8c\x66\x97\xc5\x0c\x1c\x91\xa0\x7a\x30\x76\x5c\xaf\x53\xd2\x1c\xcd\xc1\x8f\xf1\x4c\xd7\x7f\x43\xa1\x1c\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\xb4\xb7\xd5\x28\xd0\xde\x57\xd4\xc9\xc1\x6d\x74\xb5\x52\x2e\x63\x20\x25\x5a\x39\x69\x63\xc6\xfe\xe7\xaa\x32\xf8\xf5\x58\xff\x3c\x45\xc9\x3b\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\x4b\xbe\xe8\x56\x03\x45\x5d\xa3\x50\x5e\x5f\xeb\x65\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\x30\xe7\x69\xef\x6e\x9f\xca\xa9\x51\x36\xa0\xbb\xbb\x0f\x3d\x52\xf9\x78\xc7\xc3\x87\x91\x7f\xdb\x28\x6f\xee\xd8\x76\x4c\xb3\x22\x88\x12\xbf\x73\x5b\x07\x91\xfc\x36\xa9\x86\xa8\x39\x50\xdf\x4c\xaf\x26\x6b\x51\xc4\xca\xa8\x75\x05\x51\xd0\x6c\xce\x8e\xfc\xd1\x04\x6a\x36\xfb\x1d\x0a\x97\xb5\x64\x1a\x9d\xd1\x44\x9a\xb4\x98\x47\xea\x32\x5f\xb9\x96\xfd\x0b\x3f\x66\x6b\x73\x5b\xc0\x32\xaf\xdc\x69\xd7\x6f\x7c\x8b\x21\x9a\x2f\x11\xee\x99\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\x8a\x1f\xac\x20\xf4\x51\x34\x4a\x04\x0a\x0b\xd7\xe9\xb0\xfd\xd0\xbe\x11\x32\x5d\xad\xa4\xd5\x9c\xd6\xda\x96\xe0\xcd\xe3\x3f\xc0\x8f\xc1\xc1\x00\x54\xe1\xc1\x9c\xad\x0a\x70\x79\x28\xb4\x59\x6c\xbc\x8c\x13\x50\x7e\xc7\x10\x47\x9f\x29\x09\x48\x1e\x25\xd3\x98\x2a\x27\x5c\x00\xd9\x37\xec\xa1\x81\x82\xb9\x8f\x19\xee\x93\x83\xb7\x76\x75\x45\x4e\xda\x27\x5b\xa7\xed\xd3\xae\x12\x06\x6b\x7c\x00\x88\xee\x99\x78\x67\x5f\xd8\xaf\x61\x89\xe8\xce\x6d\xa0\x38\x2a\xc0\x56\x61\xab\x47\x1e\x81\x31\xf6\x26\xf4\x65\x0b\x7b\xa1\xd1\x1d\x72\x04\x59\xe9\xa5\xa1\x27\xfd\x3a\x94\x9d\x16\xa4\x37\x87\x87\x12\x50\x37\x30\x18\x90\x20\x8e\xc9\x28\xc8\xa3\x31\x77\x7e\x00\x2f\x05\x76\xb6\x85\x02\x27\x4e\xd9\xc9\x58\xf6\xa6\x47\x76\xb6\xeb\x8c\x4e\xcc\x85\x2d\x38\x9a\x3c\x81\x4b\x5d\x24\xa1\x53\x10\x20\x21\x22\xd4\xc9\x69\x8b\xec\xfd\x00\xeb\x53\xa7\x3d\xe6\x89\x95\xca\xb4\x7d\x59\xdb\xaa\x1c\x60\x46\x4b\x7b\x56\xb1\xda\x71\xab\xa5\x34\xab\x7d\x7e\x19\xde\x60\x1c\xa2\xdb\xb5\xb6\x51\x54\xe4\xc1\x03\x82\xbf\x4f\xd0\x6f\xe4\xff\xed\x54\xee\xba\x2a\x2c\xc6\x60\x7a\xa3\xb9\x11\xcb\xb7\x6a\x6a\xe4\x2c\x98\x73\x23\x26\xcc\x9c\x1a\xe4\x6e\xed\x96\x33\x63\xf5\xab\x62\x62\x50\x9b\x5f\x7b\x5e\xee\x72\x62\x4c\xbf\x27\x9a\x91\xa2\x99\x80\xb3\x51\x0b\x6c\x11\xb6\x39\xd2\xf9\x21\xa9\x25\x8c\x15\xb6\xc4\x54\x6c\x3d\x56\x80\xdb\xa7\x27\x3b\x02\x54\xa6\x71\x10\x05\xb1\x75\x6a\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\xaa\x98\x72\xb1\x49\xc8\x53\xb5\x6f\x9e\x61\xde\x7c\x53\xdd\x52\xc1\xf7\x9c\x09\x17\x9f\x2d\x63\xde\x8d\x8a\x4e\xcc\xca\xf1\x74\x6b\xd7\x6b\x8d\xe6\x59\x65\xf0\xa1\x88\xfc\xd2\xf9\x35\x5c\x28\x96\xee\xf6\xc2\x55\x51\x1c\xe4\x05\x39\x39\x65\xc2\x04\xaf\xf7\x46\xd3\xbe\xee\x9f\x77\x35\x07\x20\x67\x11\xc7\xc1\x12\x1c\x68\xf4\x33\x28\xf8\x54\x34\xd0\x84\x48\x2a\x8c\x63\xd1\x11\x46\x71\x60\xfb\xa6\x89\x8c\x2e\x49\x48\x27\xc1\x32\x06\x45\x68\xbe\x64\x72\xaa\xda\x98\x5b\xc2\x47\x4d\x4f\xc4\x78\xb4\x67\xd1\x38\x46\xdd\x80\x01\xeb\x1d\x71\x45\x51\xb8\xe1\xe9\xad\xd4\xa8\x5e\x3a\x6a\x97\x3a\x62\xb4\x44\x72\x7b\x8d\x00\xc5\x0b\x52\x3e\x69\x31\x8a\xef\x91\x16\x5b\x04\xec\xbf\xd3\xd6\xa9\xa6\x76\x01\x81\xd2\xa0\x50\xb2\x8c\xed\x67\x0f\x68\x36\x1b\xa1\xcd\xf6\x2e\x67\xf5\xb7\x66\x21\xb8\x1e\xaa\x9c\x95\xc0\xf7\x06\xe1\x29\x8f\xcf\x7a\x0e\x37\xbc\x6c\x38\xc6\x78\xd9\xbf\xb0\xea\x2d\x22\x16\xdc\xaa\xf3\xef\x13\x7e\x1a\xff\xf7\x69\xb7\x5e\x44\x10\xca\x5b\xe5\xea\xa1\xfc\xde\xc1\x8a\x61\x21\xa1\x9b\xb3\x0e\xf9\xf0\xd4\xbd\xcb\xb2\x70\xe6\xb9\xb4\x10\xf7\xe8\xf6\xc6\xe0\x75\x46\x6d\xde\xca\x08\x3f\xa8\xd2\x03\xaa\xcd\x16\x6a\x5c\xc1\x2a\xfb\x6f\x6c\x4c\xbc\x4b\x4a\xff\xfc\x5e\x51\x5d\xa7\xb2\x34\x9e\x60\x67\xb2\x82\x95\x39\x85\xd4\xb3\xe4\x93\x53\x9f\x07\xf1\xfe\x62\x99\xcf\x3a\x8e\x5b\x52\xf9\x4c\x5b\xfa\x18\x75\x6b\x66\x63\x71\x1d\xae\x9f\xf9\xbc\x7f\xe2\x96\x90\x13\xcf\xce\x59\x8f\x60\xe7\xb2\x96\x6f\xd2\x5b\x79\xf4\x15\x13\x88\x3d\xf9\xde\x7a\xfe\xa0\xeb\x8e\xd4\x21\x10\xff\xdb\xcf\x9f\xcf\x1d\x6b\x8d\x1b\xd6\xd2\x89\x60\xb3\x09\x7e\x52\x2b\xe6\x63\xe5\xd9\x58\x73\xee\x08\x2d\xdd\x91\xb1\x24\x91\x3b\xdb\x26\x0e\x41\xf9\xfd\xe8\x24\x4b\xe7\x5e\x73\x03\x0e\xe5\xe3\x2d\x23\xfb\xc1\x8e\x65\x20\x64\x58\x06\xad\xf0\x60\x4a\x32\x35\xde\x72\x03\x16\x25\x06\x82\x59\x94\xe1\x4c\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\x0d\xc6\x0f\x5d\xe9\xce\x59\x20\xaf\x6c\x11\x55\xd6\x89\x9b\x77\x2a\xf6\xad\x28\x28\xf0\xa1\xe0\x77\xec\xb8\xf4\x06\xd9\xe1\x1e\xef\xf9\x6e\x9b\x33\x90\x9c\x04\x93\x82\x66\x6a\x91\xe0\xfe\xde\x68\xad\xfa\xcb\xf8\x1c\x77\x6b\xce\x51\xe2\xb0\x9b\x54\x62\x4f\xc4\x8d\x79\x5b\x56\x3f\x76\xea\x51\xea\x43\xda\x0e\x78\x53\xc9\x68\x1a\x72\x1a\xf2\xb0\xba\x6f\x0c\x76\x63\xaf\x1a\xa6\x11\xa3\x32\xbd\xcd\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\xe8\x8c\x37\x25\x7d\x3b\x86\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\x91\x0d\xb3\x77\x5d\xee\x90\x89\x87\x11\xf3\x4c\x3d\x77\x3f\x6c\x5d\xba\x19\x0f\xb6\x2b\x9c\xd9\x1b\xb8\xb6\x2c\xbe\x0c\x2e\xb6\xb6\xe2\xd8\xf0\x9c\xab\x95\xb5\xdd\x35\xd5\xaa\xde\x8b\x44\xab\xeb\xb5\x17\xbc\xe5\x57\xbb\xea\x4d\xdc\xf5\x69\x6f\xeb\xf7\x8e\xbb\x7f\x5c\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\x63\xef\x2b\xb8\xe3\xd5\x9f\xc1\x1d\x37\x7d\x07\x77\xec\x3e\x84\x3b\xbe\x83\x97\x70\xcb\x5b\x3f\x85\x3b\x6e\xfc\x16\xee\xf7\x0e\xea\x7f\xdc\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\xf6\x1b\xa0\x1d\xdf\xc0\x02\xed\xb8\xb1\x09\xda\xb1\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\xd1\xf8\xa6\x29\xa1\xc9\x19\x09\x53\x0a\xd6\x0a\xf0\x3a\x30\x48\x42\x70\x60\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\x5d\x33\x22\x45\x96\x42\x9a\x09\x20\xa9\xff\x92\xcf\xd8\xee\x13\x4d\x93\x34\xa3\xc3\x38\x4a\xe8\xda\x35\xb7\x58\x65\x78\x68\xe4\xea\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\xc7\x45\x14\x43\x9d\x5c\x9f\x09\x6b\x87\x3f\x4f\x72\xc0\xa2\xe2\x52\xb1\x44\x5d\x64\x1c\x07\x79\x4e\x4e\xa0\xc8\xa9\xe8\x26\xcf\xd0\x4c\x98\x57\xb5\x36\x80\x7b\x23\x58\xa5\x42\xb9\xca\x38\x08\xa9\xf0\x64\xdd\xdc\xc9\x39\x40\xb2\x9a\x8e\xdf\x1e\x7e\xfc\xc0\xce\xd6\x30\x09\xed\x73\x1a\xb5\x39\x69\xb6\x3f\xa3\xdf\x6f\xd0\xef\x9f\xd0\xef\xfc\xd7\x60\x94\xca\x8f\x49\x94\x24\xf4\x52\x7d\xd1\x79\x91\xc2\x53\x46\x99\xb2\x88\xc6\x66\x42\x12\x24\x66\xc2\x3c\x1a\x67\x76\x4a\x1c\x47\x4e\x21\x03\xde\x00\x95\x1f\x46\x91\x69\x16\x24\xa1\x1a\x8a\x91\xf5\x93\xf1\xf5\xd1\xf8\x7a\x67\x7c\xbd\x34\xbe\xfe\xcf\xf8\xfa\x97\xf1\xf5\xd6\xf8\x7a\x61\x7c\xfd\xc3\xf8\x3a\xe6\x5f\x6b\xa7\xe5\xae\x6b\xd8\x1c\xbd\xdb\x7f\xc1\xa6\x78\x48\x76\xb6\x7b\x2a\xf1\xc3\xe1\x4f\x6f\xf7\x3f\x1e\xbf\x7f\xf9\xe9\xf5\xcb\xb7\x3f\x7d\x7c\x35\x24\x8f\x75\x26\xcc\xea\x50\xff\xd4\x39\x25\x94\x33\x24\x5f\x88\x95\xa0\x9d\xa8\x43\xc6\xa7\x17\x47\x3f\xbf\x25\xd7\xba\xa6\x77\x47\xaf\x5f\x33\xe8\x8f\x87\x6f\x5e\x1e\x1d\x7f\x1c\x92\xad\xcd\xcd\xcd\x81\xe8\xa1\xb8\xf1\x7e\x1e\xa7\xe3\xcf\x43\xd2\x66\xac\x33\x2f\xda\x46\xde\xfe\x18\xe2\x18\x0f\xf5\xdb\x46\xfe\x00\x83\xed\xe7\x75\xbe\x4f\xee\xe3\x60\xdc\x6f\x64\x7f\xf5\x8d\x6c\x4d\xb9\x80\xc8\x67\xc1\xce\x5d\x79\x80\x38\xc8\x2e\x17\x45\xfa\xf7\x0f\x78\x73\x18\x43\xda\x23\x1d\xfe\x82\x35\xe8\x05\x18\xb0\x9c\xb6\x37\xb4\x93\xeb\xbe\x01\x28\x2e\xc7\x0f\x54\x45\x12\x79\xf0\x40\xe6\xf6\xa5\xbf\x08\x2e\x26\xcf\xe8\x45\xdb\x7e\x45\x67\x78\xfe\xfa\x81\x6c\xb3\xd2\xb6\xeb\xe3\x6d\xe9\x2e\xd2\x2c\x4e\xe4\x65\xb8\xba\xe0\xb7\x9c\xb3\x13\xeb\xb5\x1d\x07\x95\x38\x62\x9d\xeb\xbf\xa2\x17\x7d\xd0\x5e\x0a\xcf\xbd\x3e\x1b\x23\x86\x15\x39\x6c\xdd\x3a\x3f\xd1\x71\xf5\xdb\x90\x6c\x7f\xf3\x84\x97\x44\x8f\x93\xe5\x9b\x33\xc6\xf2\x14\x8e\x5b\xc3\x6f\xbe\xeb\xb5\x4c\x94\xb7\x86\x4f\x37\xaf\x4f\x7b\xdb\x8d\x7c\x3e\xdd\xf3\xbd\x7b\xbe\xf7\xe7\xe5\x7b\x9a\xed\xf1\x77\xfe\x77\xc0\xf7\x2c\xd9\x7d\x75\xd1\xdd\x23\xb9\xcb\x82\x3e\xc1\x7d\xa5\x50\x43\x36\xaf\xed\x0f\x04\xbb\xd7\xb1\x88\x26\x4f\x31\x00\xfb\x56\x22\xfc\x32\x89\x8a\x37\xc1\x42\x89\x8b\x6d\x29\x51\x0f\x39\x0f\x6a\x6f\x4a\x59\x93\x49\xed\x43\xcd\x16\xdb\x5b\x86\x9c\x3f\x44\x19\x9b\x9b\xaa\xd0\xff\x56\xe4\x8d\x82\xd1\x28\x98\x52\xd5\x12\xce\x43\xc2\xff\xd0\xce\x9b\x7b\xea\x44\xd9\x6f\xaa\xb3\xe3\xf4\x8c\xc6\xc1\x58\x36\x6b\x67\xeb\x33\xc6\xd0\x97\x3d\xf5\x57\x8e\x20\x7e\xaa\x85\xc8\x67\x41\x92\xa4\x89\x31\x6e\x13\x42\x9f\x6b\x86\x15\x10\x35\xad\xc0\xc9\x6a\xe8\x81\xc0\xa8\xd4\xe7\xa5\x61\x35\x50\x5d\x4d\xe2\xec\x36\xf4\x02\x19\x95\xa9\xf3\x98\x3d\x36\x0f\xa0\x7f\x88\x26\xa0\x41\xae\x1e\x38\x04\xfa\xd9\x84\xf5\x81\xe2\xb9\x86\x53\x5f\x65\xc5\xb8\xbf\x8d\xea\xc6\xd5\x37\x2d\x80\xca\x14\x2b\x94\x61\xc5\xfc\xc6\x56\xda\x11\xc3\x22\x08\x85\x29\x29\x98\x7a\x5e\x2c\xe8\x98\x6d\x5e\xca\x3c\x1f\x1b\x5d\x09\xef\x29\x3e\xcb\x29\x5d\xc5\x88\x32\xb8\x50\x84\xe3\xb2\x6c\xb0\xc6\xb3\x20\x0b\xc6\x05\xcd\x72\xa9\xe2\x87\x7b\x79\x51\x1a\xed\x23\xde\x36\xa2\x69\xd2\x43\xb6\xd0\x64\x73\xcd\xef\xf6\x23\x9a\xce\x0a\x22\x3d\xd2\x5a\xde\x7d\xc5\x18\x0c\x69\x93\x83\xf4\xa0\x77\x79\x0f\xda\xf1\xf8\x18\xe2\x16\x22\x00\x03\x11\x69\xe1\xb5\xaa\xba\x21\xde\xea\xf6\x7f\x49\xa3\x04\x82\x35\x90\x67\x50\x07\x19\x92\xd6\x66\xab\x4b\x36\x04\x70\x89\xe1\xdb\x8d\xe7\x02\xa2\xf5\xfc\xd9\x27\x03\x06\xb1\xe2\x6c\x88\x1e\x6e\x70\x8f\xcb\x37\x9d\x97\x32\x43\x44\xd3\x11\x0d\x6c\x9d\x60\x86\x08\x91\x3c\x5c\x1f\xd3\xd6\xbc\x70\x6f\xcd\x15\xb3\x12\x25\xac\x12\x3f\xb2\xb0\x3f\x6a\x8f\xa3\x24\xd6\xb8\x36\x3b\xe4\x1e\x48\x8e\xf9\xd6\xae\x44\xfa\x19\x0f\xf6\x3c\x18\x90\x1f\xa3\x24\x24\xfc\x71\x97\xe8\xa8\x0a\xd6\xcc\x24\x8a\x56\x4b\xdf\xe4\x83\xed\x4b\x0f\xe2\x47\xcd\xe8\x85\x34\x61\x56\x67\x2e\x96\xc6\x4f\x3d\xec\xc4\x51\x7e\x56\x62\xd5\x6c\xe3\x77\x2f\x60\x5c\x23\x6c\x6a\x76\x49\xb4\xb1\xb7\x8d\xc1\x65\x20\x64\x6c\xdb\xa1\x9b\xea\x44\xac\x1d\x11\xfa\x42\xb5\x30\x21\x1d\x5e\x64\x6f\x8f\x6c\x76\x8d\x53\xda\x28\xa3\xc1\x67\x0d\xca\x46\xb9\xb1\x47\xc4\xab\x72\x36\x83\x07\xb3\x20\x3b\x48\x43\x0a\x35\x78\x0f\x61\x6c\xb2\xa5\x39\x4e\x5e\x64\xcd\x28\x84\x4f\xda\x4a\x24\xb2\xcf\x8a\xfc\x76\x34\x02\xcd\xfd\xf7\x10\xc9\x4d\x66\x3e\x2f\xca\x5e\xa7\x9b\x93\xed\xf1\x31\xdf\x59\x64\x74\x12\x5d\xf0\x08\x5a\x9b\x17\x5d\x36\x0b\xc0\x35\xfc\xee\xed\x45\xa8\xb7\xf2\xd9\xf7\xda\x2e\xc3\x11\x34\x88\x81\x9b\x57\x06\x13\xf0\x85\xf8\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\x70\xf8\x68\x46\x19\xd4\x58\xcc\xcd\x7e\xd1\x89\x6e\xb6\xd2\xc1\x3a\x51\x44\x06\x37\xbc\xb6\x69\x10\xd6\xdf\x8d\x3d\x92\xc8\x7d\xe1\x7b\xb2\x4d\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\x8c\x22\x9b\xc7\x4b\xb7\x89\x1f\xd3\x1f\x97\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\x34\xe9\xb4\x3b\xed\x2e\xf8\x86\x7c\xb4\xe5\x79\x0e\xa9\xe0\x1d\x3b\xc1\xe2\x72\x41\x45\x73\x00\x04\x54\x64\xfa\x33\xeb\x44\xdd\x2f\x32\x7e\x70\x9f\xc1\xef\x92\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\x60\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\xd1\x8b\xa3\x21\x99\x07\x9f\x41\x35\x1c\x25\x67\xcb\x38\xa1\x59\x30\x8a\xe9\x5d\x0e\x50\x9f\x06\xec\xd7\xbb\x5b\xe4\x11\x41\xd9\xdd\x6e\x3f\xa3\x8b\x38\x18\xd3\x4e\x9b\xb4\xc1\xa9\x1b\x3b\x2d\xb4\xcc\x08\x91\x69\x72\x46\xb3\x22\xd7\xf1\x36\x41\xee\x0b\xe9\x38\x9a\x07\xb1\xcd\x64\xa3\xc4\xcf\xec\x8b\xf4\x05\x2f\xe0\x52\x5e\x65\xec\x4c\xd3\xad\x21\x17\xf0\x44\x4d\xb5\xd1\x1f\x8b\xd4\x0d\x8e\xa9\xc2\xcf\x34\x19\x63\xad\x6c\xcb\x78\xe2\x5d\x8d\x0b\xd5\x55\x1d\x99\x35\x91\x5a\x52\x77\x7c\x9e\xb8\xdc\x42\x7d\x6a\xee\x28\xc6\x61\x9f\x03\xc4\x34\xcf\x3f\xce\x82\xa4\xb3\x09\x4e\x64\x1f\x71\xab\x73\x61\xbd\x2f\x08\x6b\xab\x0b\xb1\x5b\x51\x8e\x81\xc5\xfd\x25\xb8\x69\x16\xa8\x0c\x92\x4b\xe1\x78\x47\xb8\x23\x4d\xca\xd1\xda\x17\x78\xdd\x4f\x42\xae\xfe\xe7\x34\x14\x4d\x2e\x73\xe1\x48\x3d\x27\x23\x3a\x49\x33\xda\x77\xe8\xea\x95\x38\x3a\x54\xe3\xfe\x4a\xec\x41\x35\xa4\xf5\x0a\xf6\x79\x03\xf9\x6a\xfd\x3e\x14\xa6\x62\xf3\xe0\x82\x87\xad\xbc\x88\x8a\xcb\x21\x79\x0a\x2a\x6c\xb9\xeb\x44\xb9\x70\x69\x0c\x45\xbb\xf6\x26\x83\x26\xb9\xb3\xc1\x20\x76\x8d\xa2\x78\x3a\xab\x0b\x5b\x65\x85\x21\xdd\x19\xa3\x1d\x76\x0a\xe1\x48\x6b\x7b\xab\x80\xf8\x4a\x7f\xff\x70\xf4\xb6\xaf\xb0\xcc\xdb\xd3\x0e\x2c\xc1\x75\x6c\x4e\x02\x3b\x94\x67\x8f\x2c\x82\x3c\x67\xbc\xab\x98\x65\xe9\x72\x3a\x33\x57\x80\x1a\x88\xa0\x35\xa8\xd5\xbd\x9c\xd4\x5c\xed\x11\x9c\x96\x3c\x32\x6f\xe9\x88\x25\x80\x78\xdb\x61\x56\x57\x53\xdb\x99\xb4\x1f\x45\x15\x90\xce\x7a\x94\xff\x18\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\x08\x57\x7c\x34\x39\x4e\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\xc3\x73\xb5\x6e\xcc\x5e\x1b\x0b\x88\x15\x94\x24\xb0\xe4\x99\xf0\xdf\x33\xf8\xaf\x5f\xa4\xaf\xd3\x73\x9a\x1d\x04\x39\xed\x00\x73\xe1\xfa\x5e\xcd\xc7\x18\xd4\x3f\xc4\x2d\xb3\xb8\xba\x39\x61\xff\x9f\xea\xa3\x38\x02\xc1\x7e\xbf\x31\xe1\x71\x4f\x64\x09\x3d\x27\x2f\xd9\xa8\x3a\x6d\xb8\xea\x85\x8e\x80\xad\xea\xbf\xdb\x05\xa1\x17\x51\x5e\xe4\x3d\xb2\x88\x69\x90\x83\x58\x0c\x23\x4f\x13\x85\xaa\x49\x1a\xc7\xe9\x79\x94\x4c\xa1\x64\xce\xb8\xa0\xb5\x8c\x44\x0f\x7b\xe0\x5f\xa1\xa7\x9f\x7d\x54\x44\x89\x55\xbd\x07\xef\x57\xa6\x57\xe1\xe0\x33\x85\x45\xc8\x19\x3e\x5c\x46\x47\x60\x4f\xab\x98\x2c\x27\x01\xc6\x6a\xc1\x57\x05\x9f\x78\x8e\x5a\x41\x59\xef\xd2\x3c\x8f\x46\x31\x9f\x42\x70\xa1\x21\x8c\xfa\x3e\x1c\x32\xf9\x32\x2b\xf8\x4f\x26\x52\x4b\x6c\xbd\x9c\x4c\xa2\xe9\xa5\xf8\x38\x92\xa4\xf4\x88\x7c\x66\xcd\xf3\x3f\x7d\x5d\x05\x9f\xe2\x66\x8b\x83\xcd\x35\x98\xba\x5c\xe2\x9f\xf2\x2a\x8a\xc3\x4d\x35\x9c\xba\xff\xe1\x9f\xe2\xc2\x48\xe7\xf1\x02\x8f\x1e\xa9\x85\xa9\xef\x71\x78\x81\x5f\x83\x51\x6a\xe4\x79\x4a\xc8\x7b\x18\x3e\x00\xb8\xbe\xc1\x79\xbc\x04\xea\x05\x2a\xcc\x3f\x05\x16\x10\x08\xb1\x20\xd0\x07\x5c\xa6\x08\x84\x50\x8d\xc3\x29\xfa\x5d\xc8\xdf\xb6\x48\xc1\xf9\x82\x75\xf2\xbd\x52\x72\x3a\x27\x87\x71\x90\xb0\x93\x41\xa0\x58\xb3\x48\x17\xba\xb2\x34\x23\x01\x79\xf5\xf2\x9f\x70\x08\x97\xd2\xda\x9d\x31\x14\xb5\xcf\xca\xa3\xdd\xcf\x33\x2a\xfd\xec\x05\xe8\x2a\x57\x44\x41\x41\xc1\x02\xd8\x7a\x0a\x72\x72\x4e\xd9\x02\xd1\x0e\x56\xe4\x30\xd6\x90\x34\xf4\x33\x35\x8e\xe4\x72\x9c\x98\xa5\x70\x51\x87\xd5\x2c\x99\x04\x16\x8a\x78\x09\x1c\x35\xd6\xe4\x54\x9c\x3b\x59\xf2\x10\xde\x86\x45\x05\xe4\x99\xd1\xc8\x10\x7f\x21\xc9\xaa\x76\xf9\x06\x1c\xc7\x9e\x15\x7c\x4e\xa3\xfb\x05\xfb\xdf\xb2\xc4\x8b\xb4\x6a\x81\xa3\xf3\xc2\x6f\xb6\xd4\xd9\x6a\xfb\x1d\x17\x3b\x20\xe4\x6e\x96\x7a\x11\xcd\x69\xfe\x7b\x2c\xf3\x44\x28\x17\xd9\xe2\x56\xaa\xaa\x9c\x1f\xf3\xd9\x16\x4d\x94\x29\x8b\x43\x0d\xaa\x23\x8d\x68\x42\x53\x81\xbc\x3a\x64\x53\xaf\x49\xc1\xac\x4d\x39\xb9\xd2\x15\x68\x00\x85\x7e\x6c\x7b\x63\x4d\x42\xcd\xf1\xe7\x1b\x26\x03\xc2\xaa\x97\xe5\xc5\x8f\xab\x2b\xb2\xb9\xeb\x3d\xdb\x88\x7a\x9d\xb3\x09\x4f\x37\x0e\x44\x02\xe5\xb2\x27\x0f\x1e\x10\xf1\xdb\x27\xf3\xb3\x26\xed\x5c\x7c\xc0\xf0\xb9\x40\x33\x44\x31\x51\x58\xa9\x44\x36\x2f\xda\xbd\x76\x1b\xdf\xb7\x58\x8e\xd2\x7c\xa5\x31\x9d\x94\x8a\x74\x89\x0c\x1d\xeb\xa1\x14\x45\x27\x1c\x4c\x06\xf1\x50\x27\x31\x61\x35\x09\xb0\xc5\x79\xda\xce\xc9\x58\x85\x74\x71\x48\xcb\x8c\xf8\xd2\x84\xbe\x4a\xa8\x06\x9d\x91\xcd\x3a\x4d\x7d\x97\x41\x32\x0c\x7c\x84\x28\xcb\xb7\x5e\xe1\xc5\x77\x07\x39\xad\x53\x05\xb0\x46\xa2\x76\xea\x5a\x93\x5b\xfe\xb5\x60\x96\xfb\x8b\x78\x99\xeb\x2e\x88\x6f\xaf\x77\x43\x05\x64\x2a\x92\x66\x74\xfc\x39\x97\xa7\x26\xce\x22\xe5\x2d\x67\x2e\xde\xca\xc5\x97\xe0\xc6\xd7\x1b\x8c\x98\x93\xfc\xd8\x1b\x88\xd8\x0c\x29\x8c\x1a\x60\xeb\x3f\x40\x05\xb0\x63\x3b\x08\xae\x24\xa6\xce\xaa\xdc\x98\x39\x51\xde\xd2\xa0\x0d\xfe\xb3\x79\x71\xb2\xf9\xe8\xbb\xe0\xd1\xe4\xf4\xcb\xe3\xcd\xeb\xff\x19\x44\xfd\x82\xe6\x85\x02\x5f\x61\xf0\x15\x63\xfe\x4a\xa3\x6d\x30\x4e\x50\x00\x0c\xfe\xd3\xd9\xbc\xe8\x3e\xab\x1c\x28\xa6\xc0\xc1\x40\x07\xcb\xe2\xe1\xb0\xa0\x7b\xdc\x85\xb0\xb0\x3a\x9c\xc3\x43\x5e\xb6\x21\xa3\x61\x9b\x14\x2c\x3c\x01\x12\xd3\x57\x85\xb7\x33\x66\x5f\x18\xa3\x43\x60\xfb\x8f\x7e\xf4\x82\x59\x5d\x86\xd8\x5d\xed\x1c\xbc\x1d\xe7\x73\xf6\xef\x38\x58\xe4\x20\x3c\x88\xdf\x3d\xec\x9e\xd1\xee\x2d\xf7\x3a\x8f\x3a\x6b\x54\x7e\xa4\xf6\x76\x8e\x19\x1a\x8c\x67\x64\x1c\xe4\x4e\x35\x51\xce\xa9\x64\x39\x17\xb3\x83\x48\x89\xaf\xb1\xe6\x04\xc5\xdb\xca\x97\xf3\x39\x0d\x4b\x69\xcb\x6a\xee\xae\x69\xcc\xaa\xbe\x8a\xd6\x06\x03\x3e\x20\x0b\x39\x81\x2a\x29\x7e\x39\x1b\x90\xd6\x86\x08\x88\x57\x41\x0e\xae\x68\x66\xc1\x8e\x6c\xc4\xd4\xa4\x48\x59\xc7\xe7\xee\xe5\xf1\x26\xdc\x50\x12\x8b\x3c\xc0\x75\x77\x31\x23\x31\x85\xc7\xd4\x28\xfe\xde\x62\x41\x33\xd6\x5b\x39\x0f\x09\xc4\x2e\x9c\x46\x3c\xbc\x5d\x90\xd3\x79\xb0\x60\xf3\xb1\x65\xe8\xf9\x3a\xca\x7e\x01\x75\x1a\x9c\xb2\x6d\x3d\xe9\x92\x1f\xc8\xb7\x6c\x37\x17\x59\x27\xd1\x69\xbf\x48\x8f\x59\x43\x42\x13\xb4\xbe\xb7\x87\x32\x81\xe2\xab\x2b\xfc\x7e\xcf\x53\x23\xd6\x2d\x59\x35\x96\x78\x0a\x47\x6b\x52\x73\x7c\x83\xef\xeb\xe8\x0b\x8a\x4c\xdf\x88\x83\x9e\x24\xc7\x12\x5a\x2c\xd2\x3b\xa5\x45\xa9\xbc\x56\xfb\xf2\x0a\xa4\x88\x54\xc6\x8a\xfc\xec\x47\xd7\xa2\x9d\x76\x5b\xd0\x92\x4b\xa7\x06\x82\x6f\x44\xb5\x08\x68\xec\xf4\x9e\x55\x54\x41\xc7\xb2\x17\xe8\xd6\xdd\xa6\x69\x60\x79\x33\x6d\xf9\xc7\xa8\xf4\x3b\x76\xee\x99\x70\xff\xf9\xf2\x22\x4e\x91\xb8\x41\xc1\x75\x04\x6c\x92\x90\xdd\xff\x8d\xbd\x52\xea\x46\xf4\x65\xb3\xd2\xda\x9a\x2a\x69\xd3\x2a\x69\x4a\x9e\x5a\xd2\x34\x18\x69\x91\x32\x89\x32\x0a\xc9\xf6\x26\x77\x19\xf4\x48\xdc\x0f\xf2\x36\xf9\xf3\x84\xcd\x0b\xc2\x6d\x3b\x5c\xdb\xae\x5a\x52\xf6\x5f\xf6\x0b\xe7\x03\x98\x6f\x2b\xfb\xad\x66\xf4\x6b\x49\x33\xde\x6d\x4f\xfa\xd4\x95\xf8\x40\x32\x3c\xdf\x6b\xab\xb6\x59\x4f\x45\xe2\xee\xcb\x57\x9f\x09\x21\x23\x2f\xc2\x8d\x92\xaa\x51\x3f\xa6\xea\x91\xc7\x9b\xfe\x4b\x02\xe9\x87\x58\x1e\xa6\x73\x2d\xe5\xd6\xc7\xd8\xf4\x9e\x24\x7d\x37\x5f\x46\xdc\x4d\xbe\x93\xf9\xce\x80\xa4\xc3\xbb\x61\x89\x85\xb2\x6f\x49\x5e\x04\xc9\x98\x71\x11\x5d\xf8\xea\x4a\x21\x4d\x14\x86\xc7\x6b\xf0\xcb\xf0\x9b\xe1\x4d\xe5\xa6\x11\xc0\x8b\x54\x95\xed\xa6\x88\x92\xe7\xe1\x3a\x2c\x7d\x70\x6c\x8b\x1a\xa2\xc8\x13\x21\xc9\x8b\x1f\xc1\x5a\x45\xcf\x60\x34\xbc\x6f\xed\xbb\x43\x0f\xef\x4b\x63\xdc\xc8\x1e\xd7\x63\xe7\x47\x6d\x43\xb2\x2a\x7e\x64\xd1\x1b\x61\x48\x96\x68\x37\x1c\x11\xeb\x53\x51\x3f\x1c\xde\xf5\x1b\x0c\xe6\x48\xf4\xad\xe1\x62\x60\xf2\x45\xb2\x8c\x63\x08\x92\xd0\x71\x57\x08\xd8\x6d\x83\x0a\xc3\x33\x76\x71\x5d\xdb\x70\xe4\x23\xde\xd9\x06\xec\x80\x03\xde\x84\x19\xf0\xa4\x1b\x4d\xa4\xe8\x5e\xd3\xd1\x80\x07\xc0\xfa\xb1\x38\x01\x35\x1a\x8e\xc4\x0d\x8a\xd1\x90\xa5\x41\xc1\xca\x31\xd8\x07\x12\xbe\x8f\x82\x89\x5c\x2a\xa9\xce\x1c\xc4\xdf\x73\x73\x5d\x69\x03\x84\xca\x31\xb0\x62\xf6\xa3\x01\xe5\x39\x29\xbb\x74\xf7\xa9\xf5\x75\xb8\x98\xe4\xaf\x70\xb5\x2d\xeb\x35\x19\x43\xd4\xa7\x0e\xf5\xec\x6d\xf8\x38\xba\xca\xa8\x03\x31\xee\x97\x6c\x02\xe9\x72\x4e\x46\x71\x3a\xfe\x4c\x66\x34\x08\x69\xc6\x3e\xd2\xb9\x6d\xb4\x11\xe5\xcf\x59\xb2\x4f\x68\x98\xd1\x0b\xe5\x16\x1d\xca\x92\x49\x14\x17\xb6\x32\xd3\x43\xb0\x00\x6b\x78\x1f\x66\x29\x95\xe7\xfc\x6f\xb6\xb6\xf5\x41\x9f\x83\xd7\xe0\xa5\xfc\x98\xce\xeb\xc2\x55\xf9\x4e\xe9\x2e\x94\x2f\xe0\xb0\x3e\x69\xaf\xb9\xfd\xb8\xc1\xcc\xc4\x29\x13\xf3\x16\xd1\xd8\x9d\x87\x8f\x2c\xb9\x6e\x1e\x0a\x05\x54\x31\x01\x50\x93\x31\x01\x50\xac\x72\x02\x9e\x3c\xd6\xf8\xe7\xd0\x37\xc6\x3f\x54\x85\x6b\xf2\xa1\xdf\x01\xba\x11\xf6\x4b\xfc\x8e\x08\x91\x6f\x28\x7f\xf4\x64\x2a\xbc\xf9\x19\xaa\x5f\x3c\x1d\x04\xc3\x21\xff\x4f\xa6\x08\x03\x92\xa1\xfe\xc9\x73\x90\x71\xc9\x10\x7f\xc8\x72\xc7\xc5\xe4\xe9\x50\xfc\x2f\xd3\xc0\x5c\x65\x28\x7f\xe8\x7a\x38\xac\xfc\xa5\xd3\x05\xbc\xfa\x29\xea\x71\x6d\x6e\x87\xbe\x44\x0e\xed\x9a\x72\x0e\x3d\x69\x06\xac\xb4\x9a\x1c\xda\x09\x72\x1c\x3f\x53\x18\xc5\xcf\x14\x8d\x01\xd2\xc4\x0f\x09\xa7\xa4\xc5\x21\xfe\x90\xb9\xa6\xca\x7a\xe8\xa4\x28\xac\x71\x41\x7d\xa8\x7f\xf2\x1c\x24\x1d\x0f\xf1\x87\xcc\x35\x4e\x22\x43\x3b\x41\x42\xa1\x7c\x2b\xc7\x3a\xba\x0f\xdd\x24\xd9\x43\x07\xd2\x49\x92\x75\x4a\x61\x6c\x88\x7e\xe3\xfe\x26\xd3\xa1\xfa\x25\xd3\xf9\x9e\x3a\x54\xbf\xd4\xe8\xf9\x7a\x1f\xea\x9f\x6a\x4c\x6c\x97\x1c\xca\x1f\x32\x95\x6d\x58\x43\xf1\xbf\xaa\x83\xf1\xbb\xa1\xfc\x21\x53\x81\x6d\x0c\xe5\x8f\x1e\x2c\x30\xee\x9f\x4e\x3c\xea\x6e\x0d\xb7\xbe\xeb\x55\xba\xb7\xe9\xb5\x96\xc5\xe4\x69\x6b\xf8\xf4\x9b\xeb\xd3\xde\xf6\x56\x13\x87\x0f\xe6\x12\xde\xe3\x0b\xb8\x25\xfc\x1c\xb4\x86\xa4\xb5\xd9\xdf\xde\xec\x6f\xb5\xd6\xae\xa5\x27\xb8\xed\x46\x81\x8a\xef\x1d\x49\xdc\x3b\x92\xf8\x2b\x38\x92\x10\xb5\xac\xb9\xae\xe0\xfe\x4e\x27\x93\x8c\x5e\x92\x9f\xa3\x78\xfc\x99\x92\xef\x7f\xa1\x93\x89\xed\x4d\xa2\xa1\xc3\x38\x00\x8b\x82\x84\x1c\x31\x89\x3b\x00\xa8\x28\x48\x5c\xb0\x1f\x83\x11\x03\xfb\x47\x3a\xa5\x71\x5e\xd0\x38\xa6\x19\xf9\x7e\x02\x89\x2e\xf0\x4f\xc1\x19\xf9\x39\x4d\x43\xf2\xfd\xb4\xd4\xcb\xc5\x63\xed\xdd\x47\xb8\x82\x7c\x13\x24\xc1\xd4\x74\x3d\xd1\x1f\x30\x2c\x0c\x32\x0e\x30\xe7\x00\xd2\xc5\xc4\xe1\x08\x0e\x47\x36\x70\x34\x0a\x12\x09\xf2\x12\xac\xf8\x6d\x08\x2e\x79\xe5\x03\x5a\xcc\x24\xe0\x8b\xe7\x15\x70\xe1\x48\xb9\x9b\x9d\x55\xd5\x97\xcf\x54\x7d\x6f\xc1\x31\x79\x19\x60\x42\x0b\x09\xf8\x8e\x66\x39\xbc\xa4\x2a\x87\x5e\x08\x10\xd5\x89\xf3\x20\x9b\x57\x75\x83\xe5\x2b\x60\x5a\x14\x10\xb4\xc9\x85\xcf\x45\x96\x04\x95\x5c\xc5\x80\x94\xec\x82\x9d\xa8\xb4\x6f\x8f\x28\xb6\x2a\x44\x51\xe5\xcb\x5d\x84\x70\x20\xe9\x8c\x49\xbc\xdb\xa0\x49\xe8\xe9\x1b\xcf\x90\x60\xcf\xe1\xc4\xe4\x42\x8d\x58\xba\xc2\x64\x96\x2e\x68\x56\x5c\x7a\xe0\x16\x22\x4b\x82\xbe\x2a\x8a\xc5\xbb\x2c\x3d\x8b\x42\x2f\xb9\xb1\x85\xba\x10\xd9\x8a\xd8\x16\xe3\x8a\x12\xd1\x62\x6c\x17\x68\xe6\xd0\x70\x6d\x4d\xc9\xea\x3f\xd3\xd1\x0e\xe9\xc8\x6a\x4c\xa7\xbc\x99\xbd\x42\x12\x7a\x6e\x2d\x1b\x5d\x12\xf9\xe7\x15\x91\x56\x51\xcf\x25\x14\x02\xa2\xfc\xa9\x0b\x3d\x67\xcb\x05\xfc\xf4\xe3\x2a\xc2\x91\xc8\x7c\xf1\xdc\xc9\xcb\x67\xb2\xe4\x87\x99\x5b\x32\x81\x35\xc0\x72\xdf\xd2\xc2\xc9\x5d\x68\xc2\x67\x20\x72\x1d\x38\x70\xa3\x5f\x7f\x95\x6d\x30\xba\x76\xfb\xa0\x09\x1c\x80\xc4\x67\x07\xc3\x68\xca\xd6\x47\x8d\x60\x11\x0d\xd5\x66\x28\xfe\xe7\x47\x0e\xdc\x49\x81\xad\xdc\x28\x8a\xc9\x67\x68\x7c\xf5\x14\x0c\xa2\x97\x21\xfe\x70\x9a\xf8\xa4\xd6\x00\xff\xe1\x0c\x50\x00\x74\x74\xfb\x82\x9c\x23\x9a\x0f\xd1\xef\x0e\x37\xe6\xb9\xee\xee\x32\x89\x69\x30\x00\x0f\xbc\x39\x25\x7a\x0c\x29\xdf\x89\xc1\x25\xd0\x1a\x23\x37\xcf\xf8\xea\xc6\x56\x3a\x2e\x26\x34\xca\x3a\x65\x38\x4d\x8a\x29\x0f\x87\x0c\xae\xa7\x71\x5c\x78\x65\xd2\xf6\xf4\x25\xa3\x3c\x56\x84\xee\xc5\x67\x4a\x17\x87\xf9\x87\xcb\x64\x1c\x25\xd3\xca\xae\x40\x59\x0b\xbe\x19\x05\x7a\x3a\x82\xf9\xc2\x73\x6d\xbf\x62\x41\xc9\x57\x30\xdc\x9b\x14\x7c\x79\x60\xe4\x8b\x59\x09\x05\xdf\x1e\x38\xf1\xec\x5a\x82\xb1\x4f\x07\x0a\xbf\xc0\xe5\x80\x2a\xc5\x0b\x6b\xd4\x29\x13\x3c\x6d\xeb\xe7\x54\xb2\x79\x91\xe2\xad\xd5\x86\x46\x69\x9e\xba\x31\x2e\x65\xed\x55\x38\xe5\x16\x8e\x12\xf2\x67\xea\x1f\x19\x86\x12\xdf\x0e\x1c\x36\x6c\xe1\x90\x2a\xc5\x03\xeb\xde\x0a\xcb\x32\x07\xf6\x6d\xa1\xd3\xe7\xb2\xb2\x4e\x8e\xa7\xdd\xc3\xe7\xfb\x6f\x51\x63\xec\xd3\x81\xd2\xde\x69\x38\x98\xf8\xf6\xc1\x49\xc7\x29\x0a\x10\x12\xd8\x2e\x66\x2f\x7c\xbe\xf5\xe3\x87\xdc\xfc\x52\xc8\x74\xae\x68\x5e\xd7\xc1\x9d\xb4\x0d\x59\x76\x7d\x1a\x46\x19\xa8\x8a\xc7\xc1\x02\x1e\x5f\xa0\x0b\x4c\xcf\x8c\x1e\x1e\xec\xbf\x33\xd6\x3e\x2b\x87\x2d\xe4\x22\x2e\x4a\xb2\xe5\xcb\xa4\x4a\x9e\x6f\xbc\xf5\x64\x10\x7d\xd1\x8c\x5c\xd9\xe0\x4f\x46\xf1\xdf\xaa\x80\xa3\x27\x8a\x77\xc3\x5e\x27\xc4\x91\x8e\x79\xe7\x9c\x80\x0e\xa6\x2d\xf7\xa4\x24\x0d\x69\xbb\x67\x40\x4c\xc1\x2e\x64\x48\xda\x4c\xe8\xf8\x34\x8e\x23\x9a\x14\xff\xe0\xe0\x6d\x7d\x27\xdd\xed\xdd\xa4\x35\x5a\x9c\xa7\xd9\xe7\xb2\x06\x13\x5a\x7c\x12\xa0\x16\x88\x19\x2f\x60\x68\xaf\xf2\x5b\x76\x8b\x0a\x85\x76\x59\xbf\x68\x31\xfb\x04\x73\x3d\x4e\xe3\x7f\xfc\x0e\xfd\x3b\x9f\x45\xf9\x42\xb9\x46\x76\xba\x97\xcf\x66\xb7\x46\x1b\xfc\x3c\xf5\xee\x25\x51\x7e\x90\x26\x09\x77\xd9\x84\x96\x5b\xd7\xa0\xbd\x8e\x77\xbb\x7c\xf0\xc0\xbb\x8d\xe2\x2a\x3b\x5d\xff\x0e\xc6\x9d\x14\x48\x99\xbc\x94\xe6\xc1\x38\x14\x02\x27\x08\x89\xc6\xab\xb7\x65\x75\x4b\x67\xa2\xf8\x84\xc0\x55\x4e\xc6\xc1\xa2\x35\xdc\xde\x64\x49\xf8\x48\xd2\x1a\x6e\x6f\xb1\x34\x7d\x1c\x68\x0d\xb7\x1f\xab\x14\x2e\x3a\xb5\x86\xdb\x4f\x55\x12\x16\xee\x5b\xc3\x9d\x6d\x95\xc1\x56\x78\x6b\xb8\xb3\xa3\x13\xb4\x50\xdf\x1a\xee\xe8\x4a\xf5\xb1\xb0\x35\xdc\xf9\xd6\x49\xa6\xc5\xac\x35\xdc\x79\xea\xa4\x27\xb4\x68\x0d\x77\xbe\x73\xd2\xa5\x20\xdc\x1a\x3e\xde\x74\x32\xf3\xd9\xac\x35\x7c\xbc\xe5\xa6\x33\x59\xb8\x35\x7c\xac\xbb\x2f\xcf\x38\xad\xe1\xe3\x6f\x54\xa2\x79\x70\x6e\x0d\x1f\x3f\x51\x59\x52\x6a\x69\x0d\x1f\x7f\x5b\xad\xdb\xbb\x3e\xed\x6d\xef\xdc\x6b\xde\xee\x35\x6f\xff\x2d\x9a\xb7\x20\x8e\xc1\xbf\xc4\xed\xdc\xb8\x22\x05\x97\xa3\x0a\xf1\xe9\x42\x64\x94\x98\x97\x67\xdc\xa2\x1f\xe9\x18\xa0\x37\x12\x4e\xc7\x8c\xa9\x0b\x8e\xe4\xea\x69\xbc\x8a\x9a\x1f\xe1\x72\xd7\xaa\x0c\xd2\x24\xc4\x39\x0f\x7d\x64\x82\x48\x56\x24\x32\x95\x73\xd7\xfd\x38\x36\x86\x62\x0a\x46\xe6\xd1\xaa\x07\x37\xf5\x3d\x62\x99\x96\x95\x28\x3d\xcc\x04\x7c\x44\xfe\x85\x5f\xce\xb3\xff\x70\xb2\x63\x2e\xc9\x37\x21\xa7\x87\xd5\x51\xbe\x2d\xa9\x55\xba\x03\xdf\x53\xbf\xae\xae\x20\xfc\x0d\xb1\xdd\x3e\xb0\x44\x48\x3d\x69\x33\x29\x14\xc2\x0a\xb4\x7b\xa4\x5d\xa4\xfc\xe7\x69\x9f\xa3\x19\x85\x3b\x9c\x78\x6e\x43\x45\x33\x27\x93\x53\x30\x70\x51\xf6\xa1\xe2\x86\xb4\xeb\x89\x99\x6d\x55\xc3\xfa\xc3\x8a\xef\x21\xe2\xe1\x1e\x74\xa0\x23\xfc\xbc\xa4\x63\xe0\xe9\x06\xa5\xcd\x82\x7e\xb7\x05\xae\x28\x34\x5e\x0d\x3c\x9b\x8f\xbb\xb0\x73\x8a\x2a\x8c\x7b\x82\x16\x87\x41\x11\xc8\x11\xb0\xdf\x7d\xf6\x0f\xd9\x43\xbf\xaf\xae\xc0\x28\x56\x01\xc0\x55\x72\x2e\x41\xc4\xd7\xd5\x95\x0e\xbe\x09\xda\x46\xd6\xb4\xbc\x23\x47\x80\x27\x9b\xa7\xfd\x9c\x31\x04\xe5\x61\x9d\x41\xcf\x85\x80\xa3\x29\xcc\x9d\xae\x5f\x3c\xd3\x85\x5b\xd9\x13\xa6\xb6\x42\xba\x73\x2f\x6d\x3b\xbf\xa8\xe7\xe9\xdd\x93\xcd\x53\xf4\xf0\x6a\x1d\xda\xef\x92\x2f\xf0\xd4\x21\x48\x92\xb4\x20\x93\x28\x09\x79\xbf\xa2\x64\xca\x1b\x7a\xa6\x9a\x1f\xa7\x49\x9e\xc6\xb4\x7f\x1e\x64\x49\xa7\x8d\x4b\x70\x67\x39\x8c\x15\xc7\xe9\xb4\x8d\x4c\x5f\x45\x8f\x19\x2a\x1c\x87\x4b\x54\xb0\x21\x1c\x98\x0b\xe6\xae\xe3\x5b\x9d\x3d\xde\xad\x9e\x49\x10\xe6\x11\x0a\x6a\x94\xbe\x0e\x61\x8a\x1b\x2c\xc7\x0b\x3a\x66\x12\x80\x67\x3d\xf6\xc0\x21\xd3\x28\x18\x7f\x56\x21\x44\xc1\x13\x81\x38\xec\xca\xeb\xd6\x4e\x90\x4d\x97\xf0\x12\xe4\x44\xfd\x42\xce\x78\x4c\x2b\x74\x59\x23\x84\x7e\xae\x2c\x86\xdd\xc6\x75\x1c\x08\x36\xf1\x5b\xa6\x1b\x0b\xcd\x36\x92\x65\x1c\x3b\xe8\x4e\x25\xa5\x09\xe7\x77\xfa\x00\x2c\x21\x26\x28\xc8\x1a\xd7\xcc\x02\x26\xfb\xa3\xc8\x54\x1a\x22\xf1\x9b\x73\xf6\x4e\xda\x83\x83\x52\xbb\xe7\x65\xac\x3d\xc9\xde\xd9\x61\xab\xd3\xed\xe9\x86\x10\x86\xeb\x67\x2a\x28\x8a\x60\x3c\xfb\x98\x1e\x48\x3f\x58\x78\xca\xa4\x73\x2c\x7c\xe6\xd6\x53\xcb\xc7\xcd\x3f\x9d\xe1\xc8\xa2\xfd\x20\x8e\xd5\x7e\x22\x80\x4b\xce\x14\x4e\x37\xd5\x01\xc3\x73\xc2\xf0\x1e\x31\x80\x54\x5b\xc3\x6d\x90\xee\xf9\xaa\x6f\x0d\xb7\x41\x76\xc7\x21\xdb\x76\x00\xd8\xda\x08\x5b\xc3\xc7\x3b\x4c\x64\x7e\x7c\x2f\x32\xdf\x8b\xcc\x7f\x6d\x91\x19\x45\x7b\x81\xb3\xf7\x5d\x85\x7b\xf9\x7b\x9e\x26\xd9\x62\x6c\xca\x9b\xbf\xf0\x44\x75\x75\x98\x65\xa9\x2d\x02\xf3\x34\x25\x89\xba\x2a\x0a\x36\x58\x43\xc8\x74\x64\x4c\x40\xc7\xa7\x52\x49\x53\x64\xe4\x22\xae\x77\x8d\x9f\xc0\x20\x0c\xa5\x4b\x47\xc6\x8e\x45\x61\xf0\x92\x0d\x5d\x13\x09\x96\x45\x60\x10\x86\x1e\x1b\x5b\x22\xc6\xcf\x0b\x15\xda\xba\x75\xb0\x06\xe3\xc4\xac\x38\x0c\x7d\x32\xb7\x6f\xe0\x39\x0f\x0a\x2e\x21\x6a\x47\x24\x99\x76\x55\xff\x05\x8c\xb7\x6b\xbe\xfd\xdc\x74\x2e\xa0\xf0\x6b\x74\xd3\x9d\x02\x7d\x4f\x94\x84\x5c\xcd\x24\x61\x7b\xa8\x6e\x9a\x65\x3d\x21\x89\xe6\xae\x4c\xcc\xc9\x87\xff\x12\xc2\xa2\x06\x10\xf8\xc1\x1e\x26\x15\x2a\x7b\x04\x5e\xb7\x97\x3c\x60\x13\x55\x9e\x00\xcc\x29\x3e\x1e\x94\x0a\xec\xbc\x48\x49\xb5\x4c\xac\x91\xfd\x11\x95\xf6\x1d\xd9\xc7\x2e\xb0\x2e\x16\x51\x3f\xca\xff\x11\xc4\x51\xf8\x9e\xe6\x8b\x34\xc9\xa9\x68\xca\x79\x7c\xe7\x8c\xc1\xdf\x5e\x87\xaf\xb1\xfe\x61\x72\xe6\xad\x75\xd7\xa9\xf4\xda\xed\x5f\x69\xe5\xdc\x65\x93\x33\x58\xbe\xe7\x82\x6b\x08\x5f\x86\x68\xbc\x2f\xfa\x00\x4e\x23\x70\x82\x13\xc4\x5e\x4f\x85\x3a\xdf\x10\xbf\x28\x01\x94\xa5\xf5\x93\x7c\xf0\xad\xe1\x36\xe8\xd1\xc4\x8a\x6c\x0d\x77\xc0\xea\xad\x51\x90\xef\xfb\x0d\xff\x7e\xc3\xff\xf3\x6e\xf8\x7a\xbf\x57\x62\xf9\x1d\xa9\xc8\x1a\xea\xaa\xd8\x89\x27\xb3\xc0\x72\x21\xeb\x0f\x20\x73\x55\x75\x9a\x84\x43\xef\xa6\xb0\x1e\x4c\x3e\x88\x12\xd0\xfb\xe8\x10\x82\xc0\x94\xc6\xd0\x88\x38\xee\xdb\x3f\xb9\x7a\x09\x3f\x32\x83\x6d\xde\x7e\xa7\xcc\xe1\x0e\x34\xd8\x3b\x09\xa5\xe4\x02\x30\xf6\xbd\x26\xd2\x95\xb3\x99\xea\x6d\x40\x38\xfb\xf5\x57\x6d\x3e\xf5\x1c\x45\x3d\x51\xce\xba\xd5\x09\x46\x91\x47\x0d\x82\xdc\x3e\x13\xcb\xcf\x32\x8f\xef\xbd\xb7\x47\xda\xa8\x4f\x6d\xf2\xe0\x81\xe1\xc7\x19\x9d\x9b\x79\xb3\x86\xb3\xff\xeb\xae\xb5\x0d\x57\x35\xe8\xf1\x0c\x4d\x3a\x90\x58\xb2\x5d\x43\x1e\x77\x18\xed\xd9\x19\xac\x8a\x18\x58\xee\x69\x1a\x68\x4f\x1c\xde\x39\x42\x39\xa8\x42\x23\xd2\xf2\x48\xed\x55\x03\xe9\x51\xc5\xf3\x12\x9e\xa2\xf8\xd1\xda\xfb\xb2\x29\x08\x43\x49\xc3\xb9\x3e\x86\x63\xda\x90\x69\xd7\xaa\xa6\x52\x7a\xe2\xa4\xe2\xaf\xb2\xf2\x64\xaf\x8f\xeb\x37\x27\x14\xf4\x0a\x71\x95\xd9\xc7\x9a\x2a\xa5\xfd\x51\xfd\xf9\x44\x8b\x99\x54\x37\xeb\x4e\x9a\x7e\x2f\x6a\x55\xa9\x13\x47\xcd\xa1\x11\xa0\x55\xa5\x0d\xe6\x95\x73\x8b\x46\x93\xca\xf9\xcd\xdd\xcd\xa8\x5d\x5f\xbd\xa2\x46\x32\xbc\xbb\x98\x5b\xce\x7b\x2d\xb5\xb2\xe0\xac\x42\xdb\xa8\x78\xac\x39\x79\xae\xde\x8a\x77\xac\x74\x3a\xf7\xe3\xb8\x72\xba\x00\x48\x5c\xf4\xac\x4c\x60\x5c\x15\x5a\xd3\xc1\xd5\xa9\xcd\x78\x14\xe8\x2a\xd5\xca\xa8\xad\x8a\xdc\x94\x9b\x1c\xb0\xfd\x93\x93\x3e\xa5\x45\x2e\x8c\x57\xe2\x4b\x12\xd2\x45\x9c\x5e\xd2\x50\x9a\x08\xc2\xf3\xc1\xf1\x2c\x88\x12\xfb\xb9\x1a\xd4\xf6\x63\x9a\xc9\x1e\x79\x7c\x0f\xc8\x03\xab\x8f\x24\xe5\xba\xbc\x56\xaa\xc5\x35\xc3\x43\xee\xb1\xbc\xdc\xd0\xcf\xda\x4a\x5a\xc4\x06\x0f\xb2\x25\xa4\xb0\xd4\xe4\x0b\xf1\x9a\x21\x90\x8c\xa3\xe6\xfd\x11\x82\x94\xef\xc9\x87\x65\x90\x3f\x18\x90\xf3\x20\xe2\xea\x72\x10\xb9\x16\x85\x56\xc1\xca\x9b\x32\x73\xde\xc5\x52\x50\xf1\xa2\x75\xc7\x68\xd7\x74\xbc\xbc\x4e\xe1\x69\xb2\xd1\xbe\xbd\x2b\x41\x7f\x37\x36\x76\xcd\x63\xd3\x60\x40\xf2\x22\x5d\x70\x5d\x6d\x94\x4c\x49\x30\x61\x5d\xf9\x66\x93\xcf\x55\x4e\x3a\x45\x34\xa7\xe9\xb2\xe8\x3a\x47\x47\x8e\x80\x1f\xc8\x37\x9b\xde\xc3\x22\xef\x7d\x9f\xd5\xfe\xb3\xa8\x5c\x87\x54\xe8\x92\x2f\xd7\x9e\x33\x9d\x8d\x40\xfe\x60\xcf\x7b\x0e\x55\x33\xe2\x3d\x6d\xea\x93\x9f\xf6\x0b\xac\x18\x13\xdc\x97\x04\x7c\x65\x8c\x19\x61\x83\x8f\xe0\x11\x93\x98\x97\x49\x68\x63\xa0\xed\x3b\x7c\xd2\x18\x39\x14\xc1\x7f\x8e\x37\xe2\x1b\xb7\xca\x96\x1f\xae\x59\xf9\x13\x71\xb1\x66\x50\xcd\x94\x16\x1f\x75\x53\xef\x39\xa9\x69\x8e\x82\xba\xf1\x2a\xc8\x67\x98\xa8\x7a\x92\x30\xbb\xfe\x23\x7c\x34\xe9\x08\x00\x3f\xb5\x79\x0b\x79\x3b\x08\x11\x8c\x44\x5d\xfd\xb1\xb9\x00\xcd\x1e\x41\x98\x23\x7f\x77\xe4\x5f\x99\xf3\xf6\x27\xca\x79\x7b\xd9\x5f\x34\xe9\x98\x14\x77\x75\x45\xd6\xa1\xc5\xca\x62\x44\xb1\x6e\x0f\x6d\xe2\xbf\x9b\x2c\x01\xfc\xd7\x70\x39\xd8\x43\x4a\x43\x14\x21\x7a\xa7\x72\x66\xe4\xdf\x60\xa0\xee\xf9\xe2\x74\x8a\xa8\x16\x8e\x15\x92\x8d\xaf\xb7\xbb\x35\xcd\x13\x43\x54\x53\x1c\xb5\x64\xaa\x1b\x54\x36\x18\x10\xbe\x59\x49\x71\x21\x48\x42\x22\x6e\x46\x48\x30\x0d\xa2\x44\xac\x9c\x73\x2a\x02\xfc\xd5\xfc\xf9\x65\x4f\x7b\x03\xac\xa9\xc1\x96\x75\x9c\xed\xbf\x66\x48\x63\xee\x96\x4d\x5c\x0a\xb2\x2d\x81\xed\x8e\x39\x1d\xa7\x49\x48\x18\xc3\xad\xad\x04\x91\x6e\x3d\xb1\x12\x83\x23\x82\x2e\xac\x69\x87\xbd\x5e\x8c\xee\xb8\x43\xd8\x75\x3b\x12\x25\xc4\x89\x16\x71\xca\xbc\x48\x33\x1a\x2a\x37\xee\x5c\x02\x01\x8d\xcf\x34\xc8\x49\x30\x67\x1b\x52\xdf\xcb\xaf\xed\xbf\x52\xfe\x6d\xff\x79\xbc\xcb\xdf\x45\x17\xab\x7b\x78\x5d\x9a\x5b\xc6\x31\xdc\x12\x36\x24\xd2\x4e\x36\x3d\x50\xa0\x2b\x06\x49\xe8\x3f\x06\xec\x98\x7d\xa9\x7c\x69\x58\x52\x9c\x05\x56\x73\x68\xb0\x2b\xc5\x07\x06\x38\x55\x05\xa3\xc8\xb8\x5c\xe0\x2f\x8a\xa8\x3c\xbe\x43\x5a\x30\x8a\xc8\x1e\x83\x94\x72\xd6\x43\xae\x09\xad\x1f\x93\x3e\x21\x25\x24\x40\xa2\xa9\x28\x2e\x6b\x91\x63\x4b\xe8\xb9\x4a\x92\x63\x4a\x2e\xaf\x31\x31\x58\xba\x91\x2d\x69\x53\x10\xc4\xdd\x15\x8b\x6e\x57\x14\xb5\xe5\x60\x43\xb2\x10\xbe\x4e\xa4\xa2\x38\x74\x4a\xfb\x24\x65\x01\xa1\xa4\x65\x7d\xfc\x93\x49\xaa\x2d\x3d\xf1\x50\x68\xa0\x27\x82\xa1\xd4\x77\xfd\x42\x2a\xb6\xe8\x6f\x65\x0d\xec\x4f\xfd\xe0\xd2\xb5\x3a\x45\x62\xfa\xeb\x48\x3a\xe8\xa9\xd9\xc7\x1c\x6c\x30\xe0\xa1\x15\xb5\x95\x85\x51\xa9\xb6\x95\xf8\x72\xbd\xcb\x80\x25\x96\xd6\xcd\xb6\x05\x62\x50\xc5\x70\xc6\xcd\xe0\x2d\x0e\x10\x32\x7e\x94\x10\x47\x63\x0a\x57\x0d\xda\x5e\xc3\x8a\xfe\xe7\xb3\x1d\x01\xfb\x8f\x72\x8b\x11\xe2\x58\x8d\xe4\xfd\x45\xba\x30\x1c\xcc\x99\xdd\x8b\x83\xbc\x10\x90\x4e\xd5\xfe\xee\x70\x42\xea\xb0\x82\xe0\xbc\x68\x5d\xbd\x38\x81\x38\xb4\x90\x6e\xf7\x49\xa3\xb0\xa6\x4b\xac\x21\x01\xdc\xe7\x41\x49\x7e\x20\x9b\x76\x6d\x62\xa6\x25\xed\xef\xcb\xb5\x5c\xaf\x05\x90\x7f\xb7\x52\x09\x22\x34\x59\xcc\x52\xaa\xd3\x94\xa9\x1d\x1e\xd6\xba\xd9\xe5\xfe\x22\xb8\x0c\x46\x31\xf5\x75\xcf\x3d\x0e\x70\xfb\xa9\x9c\x26\xa1\x0e\x48\x95\xa4\xc9\x23\x51\x09\x46\x87\xbd\x4d\x5c\x97\x4d\x3d\xf8\xf6\x63\x9c\xd1\xaf\x82\xed\xc8\xa5\xd2\x83\x11\xa3\x5a\xe5\x04\x81\xed\xdb\xc6\x1e\xaf\x68\xd7\x9c\xc4\xd2\x1b\x41\x7c\xa2\x35\x74\x00\x52\xee\x83\x58\x08\xa6\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\x07\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\x02\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\xcb\x63\xa4\xbe\xb4\x2a\xef\xf0\x7d\x79\x30\x30\x7c\xe9\x96\x86\x9d\x1e\x8f\xc1\x2d\x6e\xca\xe3\x34\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\xc4\xaa\x81\x08\x61\xd2\xa8\x40\x75\xa9\x27\x0b\x8c\xe8\x38\x58\xe6\x94\x9d\xc4\xc3\x34\x69\x17\xe4\x3c\x48\xc0\x28\x29\x5f\xa4\x51\xcc\xaf\xc3\x93\x82\x66\x93\x60\xac\xbc\x63\x37\x38\x8a\x37\x39\x6e\xdb\xfb\x54\x3d\x43\x24\x8e\x7f\x5d\xb5\xa8\xd1\xe2\xfc\x89\x16\xdc\x5d\x33\xdb\x20\x7b\xe4\x7c\x16\x8d\x67\x60\x35\xc0\xd6\x77\x91\x8a\x7d\x8c\x2c\xe2\x65\x5e\x7f\xf7\x2a\x18\x41\xcd\x04\x6b\xee\xe1\xb7\x64\xaa\x91\x61\x57\x17\x54\x55\xb1\x7a\x01\xf2\x36\xc2\x63\xb9\xe0\x88\xac\x95\x6f\x24\xc8\x54\x09\x31\xe6\x53\x87\x3e\xb7\x48\x6f\xce\x7d\x3d\xc7\x1e\xef\x79\xb7\xc1\xfd\x79\x19\x6f\x72\x4e\xc3\xde\x63\x70\xc9\x53\x16\xdf\x81\xd8\xdd\xfe\xb4\x61\x38\xc7\x9f\xfb\x7a\x85\x78\x4e\xd3\x5e\xbb\x25\x8b\x6e\x77\x95\xfd\xb3\x69\x2c\xd1\x1a\x7e\x5b\x66\x02\xad\x4c\x1a\x5a\xc3\xed\x1d\xd7\x26\x5a\x8c\xbc\x35\xdc\xd9\xba\x3e\xed\x6d\x3f\xb9\xb7\x7d\xba\xb7\x7d\xfa\x6b\xdb\x3e\x21\x63\x67\x61\x03\x79\x07\xd6\xce\x25\x7e\x2c\x85\x75\x25\x7f\x98\x75\x34\x91\x97\xce\xfb\xd9\x34\x1f\x96\xa8\x6e\x90\x90\x27\x8e\xb0\xa2\x12\x1c\xfb\x4e\x6e\x27\x8c\x7d\xca\x4a\x09\xb6\x71\x02\x3e\xdf\xf3\xf5\xe1\xfd\xbb\x03\xce\xdc\x6f\xd3\x01\x1e\x70\x09\x58\x2d\x85\x17\x8c\x45\x4a\xde\xbf\x3b\x10\x17\x05\xfe\x0e\x88\xf7\xe8\xe0\x45\x51\xb7\x3c\x4b\x73\x7c\xfd\xe5\x36\x7e\x70\xf4\xf6\xed\xcb\x83\x8f\x87\x47\x6f\xc9\xcb\xf7\xef\x8f\xde\x0f\xc9\x81\xd2\xff\x8e\x79\x95\xfc\x48\x1f\x52\xd2\xde\x20\xac\x3e\xb2\xd1\xee\xfb\xfb\xa0\x5d\xde\x34\x1d\xbb\x7a\x68\xcf\xb5\x08\x05\x5b\x3d\x11\xaf\xcc\xdf\x84\xb4\xa4\x1d\x12\xdb\x2a\x18\x0d\x13\xde\xa5\xd1\x3c\x0f\xa6\x94\xec\x91\xf5\x75\xf1\xd4\x90\x6d\xeb\xe2\x77\x9f\x87\x8c\x75\x52\xfa\xb2\xd8\x33\xe2\x4d\x1e\x12\x35\x5d\x7f\xff\x70\xf4\x16\x66\x25\x53\x5d\xf2\x84\x59\x15\x7d\x73\x1e\x93\x69\x1c\x88\xaa\xcd\xd1\xea\xd9\xfc\xc8\xef\xab\xf1\x78\xe7\x79\xd3\x29\xfd\x78\xf8\xe6\xe5\xd1\xf1\xc7\x21\x11\xb7\xde\x8c\xb8\x58\x27\xe7\x39\xd9\x20\x6d\xf6\x5f\x30\x9e\x31\x8e\xd1\x36\x62\xda\x08\x3f\x92\xdf\xde\xef\x56\xf7\xbb\xd5\x5f\x7b\xb7\x42\x9b\x15\x3c\xbb\xfc\xa3\x9a\xe9\x36\x7f\xcd\xde\xe8\x11\xfd\x1d\xbe\x65\x97\x4e\x87\xd8\xfa\x57\x87\x33\x1c\x93\x29\x37\x8e\x21\xe2\x91\x2d\xb4\xa5\x0f\x0b\xb6\x15\xf2\xd7\x7e\x08\xbf\x90\xb6\xbc\x48\x93\x8e\xf3\x79\xec\x0a\x52\xf1\x1e\x39\x4f\x93\x6e\xcd\x1b\x7a\x94\x99\xa4\xc9\xe5\x3c\x5d\xaa\x16\x55\x42\xc9\xe9\x4d\x22\x6d\x4a\x25\xae\x68\xc8\xe5\x01\x88\x62\xe0\x84\x6b\x12\x69\xea\x78\xf6\x3c\x4d\xe3\x6b\x08\xaf\x1a\x82\x0f\x72\xbe\x49\x50\x0e\x19\xa2\xd9\x81\x07\x22\x34\x34\x3c\xa6\xcb\x13\x1f\x44\x23\x60\x8b\x52\xd4\x3e\x58\x33\xa6\x09\xbb\xdf\x62\x10\xa6\xe7\x28\x5e\xaf\x1d\x81\x01\x21\xdf\xbd\x13\x89\x3c\xa2\x42\xd4\x17\x35\xc1\x05\x87\xf8\x5d\x62\xef\xea\x2f\xaf\x0d\x96\x4b\xaf\x88\x31\xb6\x39\x7d\x86\xdc\x07\x38\xb8\x31\xb2\x70\x1d\x6a\xf7\xe0\xde\x70\x41\xde\x0a\xca\x51\x87\xaa\xab\xf2\x12\xc4\x29\xd1\xf5\x50\xde\xd1\xf4\xda\x7c\x74\xb0\x42\x3d\x43\x2b\x84\x43\xf3\x8a\x71\xe1\xa2\xd5\xf4\xb0\xd2\x88\xa4\x2b\xf5\x1b\x0d\x27\x8f\xa6\x49\x50\x2c\x33\x7b\x38\x38\xbd\x6c\x3c\x18\xa6\x7c\x3c\x0a\xaa\x6a\x40\xe0\xc1\xa0\x79\xff\xc5\x13\x07\x49\xde\x82\x23\x05\x49\xa8\x54\x4b\x45\x0a\x41\x89\x27\x51\x12\xc4\x7e\xb3\x67\x5e\x87\xcf\xa8\x14\xaf\x6b\x2b\x4b\x54\x6f\x20\x45\xe6\xd1\x33\x9a\x5d\x16\x33\xae\xb2\x9e\x8f\x22\x60\x19\x29\x8f\x12\x0d\x7d\x13\x71\x16\x2a\xb1\xe5\xf1\x0d\x22\xba\xe3\xb8\xb6\x53\x8b\x5b\xfd\x42\xaf\x00\xef\x3d\x88\x68\x7f\x1d\xca\x41\x47\x9d\x6b\x11\xa9\xd7\x5c\xb7\x76\x1e\xb7\x9f\xa2\x72\xfe\xb2\x55\x38\x17\xe4\x8e\x3a\x25\xb5\x77\xba\xae\x4a\x53\xcc\xd3\x47\xd9\xb1\xdb\xb2\x74\x14\xc3\xa2\x92\x9f\x83\xe7\x65\x11\x4c\x5b\x94\x3f\x89\x20\xc6\x94\x65\x0d\x20\x80\xf0\xfc\x31\xba\xd1\xc9\xc9\x32\x8e\x4b\x9e\xb8\x68\xcd\x22\x71\x6f\xff\x4d\x85\x30\xd4\x57\x16\x9a\x11\x32\xad\xd1\x9c\x55\x5c\xf7\x0b\xec\x3b\x8f\x63\x3a\x7c\xfb\xea\x91\x33\xfb\xea\xbc\x6b\x07\xd7\x5b\xa9\x36\xe8\x7b\x0d\xc5\x99\x44\x32\x4e\x93\x71\x50\x74\x8c\xd9\xef\x96\x3b\xb2\x29\xe5\x7a\xc2\x8b\x4d\x39\xd7\xb3\x77\x5b\x5a\xc6\xe1\x42\x7e\xf7\xe0\xf2\x30\xc1\x15\x84\xe5\x10\x9c\x10\x78\x2d\xa1\x6a\xf6\xc1\x03\xd0\x37\x98\xbd\xa8\xde\xa6\xcb\xbd\xef\x00\x0e\xee\xd0\xfd\x4e\x90\x4d\xad\xd5\xa5\xc5\xc7\x67\x46\xc9\x21\xfe\x12\xae\x79\xb6\x90\x2b\x14\x31\x3e\x71\xff\xa2\xea\xb5\x9f\x6a\xf1\xc9\x24\x5f\x94\x94\x86\xeb\xdb\xee\xee\xb2\x95\xf9\x4b\x1a\x25\x9d\x56\xcb\xad\x5c\xbd\x8a\xe3\xe4\xc6\xf1\x84\xaf\x37\x40\x36\xec\xb0\x65\xde\xed\xe1\x1e\xe1\xab\x9a\x24\x2d\x0e\x8d\xbe\x2a\x14\x7a\x1c\x0e\x69\xe0\x86\x6d\xc3\xb3\x85\x6e\xcf\x6a\x05\xb7\xaf\x36\x12\xc4\xb5\xd3\x65\xb1\x58\x16\xaf\xd3\xa9\x66\xd7\xc2\x19\x0f\x5a\x2d\xd2\xfb\x0f\x77\x34\x83\xc4\x32\x13\x4c\x73\x6b\x18\x93\xed\x07\x8a\xc3\xf0\x5b\x2e\x83\x9f\x66\x34\x5c\x8e\x29\x9a\xab\x60\x3c\xee\x11\xe1\x8b\x12\xf3\x93\x60\x3c\x3e\x11\xc9\x9c\x27\x32\xa4\x88\x6f\x49\xe5\xcf\xcc\x29\xeb\xe7\xb3\x68\x52\x74\xba\x64\xe8\x60\x54\x66\x39\x4a\xab\x60\x3c\x96\x5a\x2a\x6e\xed\xcd\x49\x9b\xc6\xb4\xa0\x72\x1c\xda\x4b\x92\x99\xce\xa9\xea\x06\x2c\x03\xdd\x5f\x89\x87\x25\x62\x69\xb3\xad\x9e\x8b\x71\xa5\x9e\x15\xee\x4a\x2e\x32\x1a\xae\x16\x7e\x3c\x9e\x1b\x6c\xe9\xe7\x8f\xee\x92\x69\xbb\xde\x25\x53\x55\xf1\xad\x72\x23\x3b\xb3\x02\x62\x48\x80\x86\xf3\x07\x5b\xec\xb0\xfd\x3e\x39\x02\xe5\x1f\xca\x01\x54\x29\x2d\x63\xdb\xff\x06\xaf\x1a\xad\x67\x6d\xde\x27\x8d\x95\xd4\xf8\xb5\xbc\x4d\x31\x50\xf3\xe4\x5a\xc6\x01\xa5\x81\x21\xb4\x74\x82\x00\x4e\x0d\xea\xf5\x01\x60\x07\x56\x9a\x28\xbc\xa0\x27\x8a\xdd\xf3\xb6\x4f\x4b\x07\x60\x58\x4d\x78\xef\x84\x0d\x5c\x22\x97\x58\x55\x57\xc2\x75\x8e\xb2\x6e\xe8\x1b\xeb\x69\x13\x05\xfc\x6d\x9d\x5d\x0e\xfc\xba\xc9\x37\x9c\x06\x3d\xfa\xbf\xea\x48\x22\x38\x88\xc8\xda\x60\x40\x3e\x1e\xbd\x38\x1a\x92\x8c\x72\x8b\xac\x1e\xc9\x53\x61\x3a\xa3\xae\xb8\xb4\x31\x4e\xc0\x35\x5d\x7d\x56\x2e\x2a\xda\x39\x49\xe8\x98\xe6\x79\x90\x5d\xb2\xc5\x02\x21\xb0\x73\x46\x6e\x6d\x70\x58\x0c\xee\xa2\xc9\x79\x9a\x7d\xe6\x52\xde\x7c\x19\x17\xd1\x22\x46\xa1\x1c\xcc\xe0\x29\x7e\xff\x46\x83\x87\xc4\x6b\xcc\xfd\x8d\xb4\xe5\xe6\x75\x98\x66\x0c\xb2\x79\xc3\x88\x54\x37\x46\x43\xbe\x71\x98\x27\x13\x55\xaa\x2f\x71\xe4\xf3\x60\xb3\xce\x3a\x77\xe2\xc2\x9e\xfa\xce\x0f\x65\xb0\x16\x3b\x25\x8e\x81\xa3\xd9\x4f\xe1\xd0\xc9\x57\x53\x8d\x1d\xa4\xb7\x9e\xd2\x23\x94\xae\x5f\x10\xbc\x3d\x26\x07\xc0\x73\xe4\xe6\x39\x3e\x6c\xf0\x1c\xc5\xf4\x84\x49\x8f\xd9\x45\x8f\xe9\xa7\x28\x96\xd3\xc2\x0a\x15\xe3\x73\x72\x55\x79\x10\xab\x9e\xee\x88\x56\x8c\x57\xc3\x78\x86\x5c\x46\x2f\x44\x47\x39\xb9\x5c\x79\xd8\xaa\xe0\x1d\x0c\x9c\x20\xc3\x51\x7a\xd1\x37\xd8\x91\xfe\xd8\x25\x12\x40\x72\x21\xf8\x7f\x57\xa6\x2a\x96\xc3\x7f\xa8\x74\xc4\x68\xe4\x4f\x53\x8e\xa4\x17\xe2\x7d\xb7\xcb\xcd\x39\x1a\xb4\x6b\xa2\x12\xfe\x5c\xc2\x91\x5b\xc3\x1d\x70\x61\x84\xbd\x86\x33\xc6\xfc\xdd\xfd\xcd\xe8\xfd\xcd\xe8\x5f\xfb\x66\x54\x5c\x8b\x8a\x37\xbf\xff\x15\x01\xf6\xee\xd4\x65\x38\x1c\x02\x1e\x92\x83\x34\x39\xa3\x8c\x15\x05\x22\xe6\x31\x9c\x83\xe1\x2c\x00\x81\x8b\x65\x24\x17\x46\xc0\x41\x9c\xa7\x24\x88\xe3\xf4\x3c\xe7\xf1\xd9\x41\x51\x97\xf7\xd7\x58\x45\x52\xf0\x7f\x13\x5d\xd0\xf0\x9a\x67\xad\xb9\xf7\x1a\x6b\xe2\x46\xb5\x48\xed\x28\xc7\x42\x65\xa9\x0e\x9c\x1d\x53\x25\x4a\xae\xae\x64\x84\x74\x9d\xd1\x56\x3a\xd4\x76\xd7\x56\x06\xf0\xb3\x9c\x10\x91\xb8\x62\x96\xf7\xa1\x23\xf5\x8b\x46\x43\x5c\x0f\x71\x34\x01\x55\x73\x17\x6a\xdf\x74\xea\x04\x48\xc1\xf7\xf1\x93\x56\xe3\xce\x48\x46\x51\x52\xed\xc0\x91\x8b\x89\x9a\x8c\xd3\xca\xcb\x1f\xdb\x12\x36\x55\xfa\x7d\x71\xd8\xea\xb1\x49\x38\xa3\x59\x34\x01\xc7\x1e\x19\x1d\x07\x8c\xe3\xa0\x48\x35\x0f\x1e\x90\x38\xf8\xf5\x92\xc4\x69\x10\x92\xf0\x32\x09\xe6\xd1\x98\xa4\x09\xcd\xa1\x35\x31\x21\xba\x21\x11\xcd\x3a\x55\x7a\x02\x80\x92\x06\xf6\xb2\x71\x07\x8a\xcd\xd6\x94\x16\x47\xea\x90\xec\x71\xe1\xcc\x26\x46\x0b\xac\x75\xfe\x01\xb0\x32\x41\x4c\x89\x3c\x26\x97\xdf\x7a\x18\x9a\xfe\xd2\xab\x17\x9e\x9d\x9f\x47\x10\xb0\x04\xf5\x8a\x80\x0e\x22\xa7\xfc\x04\x3d\x74\x9e\x56\x71\xe1\x7d\x99\x51\xa1\x5e\xec\xc1\x05\xde\x98\xaf\x0e\x7e\x38\x9e\xd1\x0b\x9f\xda\x40\x6b\x4d\xad\x04\xcb\x15\x65\x83\x22\x86\xe6\x53\xc4\xd5\x2e\x55\xca\x5b\x0a\x7f\x19\x85\xfb\x99\x88\x4f\xce\xaa\x12\x8b\xac\x4b\x86\x72\xbd\x09\x30\x57\x56\xf2\x5d\x13\x78\xde\xd7\x41\x37\x87\x56\xb7\x7b\x0e\x1c\x5b\x02\x1a\x8a\x7d\xb9\x30\x45\x8a\xeb\x71\xf3\x03\x19\x96\x59\x02\x05\x38\x28\xb3\xdd\x1a\xdc\x5f\x0d\x57\xba\xd6\xea\xab\x72\x5d\x5f\xef\x6e\x52\xa3\x28\x65\xea\xa7\xd0\x41\x87\x53\x60\x3e\x63\x14\xe8\x41\xb8\x45\xea\x52\x55\xb3\x1f\x86\xfc\x59\x84\x52\xa2\x05\x49\x48\x72\x5a\xe4\x64\xb9\x80\x0c\x71\x1a\x01\x96\x11\x15\x34\x63\x7b\x47\x7a\x26\x84\x2d\xe1\xc7\xb4\xbf\xb6\x86\x9e\x46\xbc\x4e\xa7\xf9\x7e\xf1\xa1\x08\xb2\x62\xcd\xd6\x34\xe6\x34\x9e\xa8\xc4\x89\xfb\x80\x59\xb0\x70\xb3\x16\x23\x50\x18\x8d\x27\x8e\x13\x1f\xf9\xca\x6e\x4a\x0b\xae\xcf\x62\x85\xad\xa7\x76\xa0\x5f\xd0\xc3\xcc\xa1\x7b\x44\x9e\x3c\x2d\x9e\xc1\x5a\xe9\xfb\x18\x07\x64\x4c\x69\xd1\xb1\x1e\xfd\x08\x4b\x46\xe7\x94\x33\x18\x88\x17\x34\xf0\x4c\x94\xf5\x51\xa0\x0d\xcc\x26\xe1\xa2\x5b\x26\x4a\xb3\x23\x70\x85\xd1\xef\xf7\xc9\x2f\x4b\xee\x09\x98\xb5\xc9\x78\xaf\x73\x5e\x2e\x79\x19\x59\xf1\x2a\xf2\xda\x7e\x02\x6b\xad\x74\x35\x0c\xff\x19\x93\x67\x7a\x0f\xa6\xdc\x90\xb3\xee\x9d\x26\x7f\xbc\x63\x9a\x7d\x1a\xfd\xab\x77\xc4\xfa\xf5\x48\x77\x91\xc6\x31\x27\x1f\x3f\xd9\x0a\xda\xd4\x60\x36\x5d\x2a\x95\x08\xa8\x6d\x93\x37\xca\x0c\xd7\x20\x96\xb4\x84\x5c\xc4\x8c\xa6\xce\x9c\x4a\x23\x0b\x46\x7a\x72\xac\xbe\x49\xf0\x3d\x9b\xf2\xd1\x44\xda\xf8\x24\xdf\x94\x3a\x6e\x46\x19\xda\x4c\x19\x86\xa6\x95\xd7\xcf\xac\x04\x5d\xc9\x50\x16\x72\x49\xe7\x56\xe8\xb9\x1d\x91\x96\xea\x03\xa0\x4f\xb6\x37\x6a\xc6\x78\xde\xa5\x71\xcc\xf8\x8c\xee\x09\xa7\xc1\x21\x2f\xc2\xce\x69\x74\x4e\x93\x02\x8e\x9c\x7d\x46\x71\x30\x34\xbd\x97\x2c\x84\xa1\xfd\x09\xc7\x14\x90\xe3\x61\x78\xda\x93\x57\x54\x46\x72\x4f\x13\xa3\xc8\xc1\x7e\x8c\xb8\x82\x18\xe8\x97\x6d\xd6\x32\x6c\xa1\x43\xe2\x96\x4c\xd6\x23\x4e\x7c\x0f\xb9\xdc\x3c\xb7\x03\x3d\x71\x9a\x3a\xc8\x28\x8c\x09\xec\xb5\x0f\x3c\x2f\x1d\x81\xd9\x71\x0d\x36\xba\x70\x35\xf0\x81\x34\x7c\xab\xa8\xca\x4a\x75\x5d\xa5\xca\x1e\xbf\x52\xcd\xec\x0c\xb2\x25\x20\xa5\x2e\xe3\x4b\xad\x31\xb5\xb0\xa9\xc5\x60\x4b\xf4\x45\xd0\x0e\x1a\xcc\x04\x04\x29\x67\xde\x7d\x32\xa6\x56\x88\xb0\xac\x51\x19\x62\xcb\x3d\x28\xcb\xd7\x6c\xcf\xc9\xc2\xd7\x4e\xea\x77\x69\xbf\xfb\x09\x3d\x17\xb7\x4e\x18\x07\xd8\x59\x18\x67\x92\x51\x68\xf8\xc6\xf3\x33\xc7\x9a\x65\xdf\x19\x8f\x3c\x62\xee\x78\x54\xcb\x07\x89\xe0\xc8\xe2\x5c\x58\x41\xbd\x96\x47\x52\x97\xbd\x54\x94\xf5\x77\xa3\x5a\xef\x6c\x2c\x6d\x46\x04\xa1\xeb\x08\x10\xfb\x6a\xc8\x28\x5c\x32\xb0\x33\xc7\x82\x26\x21\x18\xb8\xa9\x49\x0e\x72\x50\xb4\x24\x39\xa3\x50\xe5\x0c\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\xa3\x9a\x09\xf2\x36\x83\x4d\xc6\xd0\x48\xb4\x3d\x31\x89\xb1\x74\x18\xc4\x19\x0d\xc2\x4b\xfd\x60\x5d\x0b\x8a\xf9\xed\x29\xcd\x14\x64\xa5\xf4\x5a\x37\xae\x68\xd2\xb1\x5a\x53\x4e\xe0\x36\x5d\x97\x5c\x7a\x65\x72\x2e\xee\xf3\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\xd5\x84\xcf\x6d\x15\xd6\x64\x73\x96\xcf\xb6\x1f\x3e\x18\x74\x97\x7b\xee\x84\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\x66\xbb\xcf\x7b\x82\x23\x53\x78\x4d\x4e\x54\x49\x5b\x28\xb7\x76\xd4\x12\x3b\xca\xc1\x80\x1c\x4e\x34\x67\x8c\x72\xf5\xae\xef\x92\x0a\xff\x2b\x24\x2a\x88\x76\xd3\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\x84\x48\xf1\xf0\xb7\xaf\xe8\xa0\xc7\x92\xc7\x33\x6a\x5b\xef\xc5\xe9\xb4\xac\x5d\x62\x31\xa6\x8a\xb3\x05\xd4\x32\x64\x7b\x42\x89\x3f\x3e\x7f\xc4\x12\x13\xc4\x39\x00\xd8\x03\x6b\x4e\x47\x8e\x9f\x29\x21\x88\x1f\xbe\xe0\x09\x43\x41\x63\x9d\x6e\x9f\xef\xc8\xe3\x40\x7a\x2c\x04\xbf\x2a\x34\x24\x6c\x75\xcf\xb2\x34\x49\x97\xb9\x72\x5f\x28\x0c\x03\xd8\x6e\x6f\xbb\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\x82\xbf\x55\x5c\x90\x5e\x9f\xf6\x76\x36\x9b\x5c\x81\xb6\x97\x39\xd7\x8b\x8f\x8b\xf6\xda\xfd\x85\xe8\xfd\x85\xe8\x9f\xf8\x42\x54\x3f\x15\x45\x2a\xeb\x9b\xbc\x17\x15\xc0\x2b\xdc\x64\xfa\x82\xbf\x35\x7e\x62\x9a\x4c\xa2\xa9\x17\x8e\x67\x49\xc0\xc3\x51\x60\x05\x75\x89\x46\x41\xe2\x09\xd4\x02\xda\x64\x1e\x69\x8a\xdb\x48\xf3\xcb\xcc\x51\x34\x15\x1e\x0c\x2c\x2b\x46\x0e\xf4\x3c\x9a\x5a\x4a\x7d\x6c\xcd\xc8\x35\xce\x57\x1c\xe2\x4a\xc1\x5e\x9b\x5e\xab\x74\x3a\xb6\xc4\x05\x3d\x63\x49\x1b\x86\x54\xc4\x7b\xe7\x7d\x86\x56\xa4\xaa\xac\x04\xdb\x55\x4a\xa0\x28\x7f\x97\x51\x71\x0d\x8a\x6e\x27\x8c\xba\x47\x3a\xdd\x6a\x60\x84\x4b\xb0\x83\x84\x70\x7f\x4f\xae\xae\xdc\x3c\x71\x36\xf5\x67\xd2\x20\x8b\x23\x56\x14\x75\x2d\x59\x2c\x8b\x17\x74\x12\x2c\x63\xef\xc5\x49\x5d\x1f\xd9\x8e\x6c\xb7\xa3\xae\x7c\xbd\xe1\x5b\x18\xc9\xf4\x43\xd4\xa2\xc7\xf7\x54\xf9\x3d\x0e\xee\x82\x35\x8a\xdf\xa2\xfb\xf6\x8b\x2e\x2e\xa0\xb0\x5a\x4a\xe6\xd8\x68\xd4\x53\x21\xca\xf6\xe0\x41\xd2\xd6\x2b\x7a\xe1\x19\xb9\x58\x55\x7c\xb0\x39\x32\x8a\x4c\x27\x24\x30\x7c\x03\x82\xe3\x49\x65\x47\xa0\xec\x02\xd8\xba\x7b\xf5\xf2\x9f\xd6\x72\x83\x3a\x98\x5c\xec\x5d\x68\x52\x97\x6f\xf8\xd8\x75\x0c\xdf\xe5\x15\xb9\xd4\xf6\xbb\x75\x7a\x23\x7f\x7f\x31\x2e\x8f\xe1\xfa\x0f\x5d\xc1\xc2\xe7\xd5\x95\x45\x43\xfb\x63\x88\xbb\x80\x1c\x9f\x61\x78\x8f\xc7\x2d\x59\x2d\xf4\x49\xb8\xa1\xf2\x5f\x3d\x9a\x72\x10\xae\xba\x48\x45\xc0\xe8\xa8\x20\xf3\x68\x3a\xe3\x82\xa3\xf2\x5e\x2c\x94\x54\x4e\xcb\x45\x5a\xdb\x6e\x91\x9a\xad\x9e\xb4\xe7\xc1\xc5\x8f\x94\xbe\xa3\xd9\x4f\x41\xde\xee\x11\xf6\xfd\x2e\x8b\xd2\x2c\x2a\x2e\x8d\xf4\x69\x90\xbf\xcb\xa2\x31\x15\xbf\xd9\x7f\x30\xcd\xec\x47\x92\x26\x63\xea\x7b\xc5\xf8\x99\x5e\x56\xbc\x63\xfc\x4c\x2f\x9b\xbe\x64\x84\x9a\x1c\x5c\xf3\x1a\xf6\x90\xdd\xc5\x0b\x3a\x8e\xe6\x41\xdc\xc1\x00\xee\x4b\x32\xf3\xb2\xf5\x6b\x13\x3b\xf2\xb9\x79\xd7\x34\xef\xab\xfa\xee\x49\xff\xa6\xd4\x7d\x4f\xd7\x7f\x44\xba\x16\x42\x91\x43\xd8\x70\xff\x2a\x83\x09\x09\xaa\xf6\x8a\x4a\x8d\xe9\xf9\xc2\x14\x8f\x44\xfa\x9a\x21\x13\xd5\x52\x70\x71\xd1\xfd\xa2\x34\x83\x17\x7d\xbc\x9d\xae\xcb\xd3\xb9\xd6\x88\x99\x00\xca\x43\x46\x2a\xf1\x67\x02\xa8\x37\x20\x2c\x1d\xe1\x02\x5e\x9b\xf9\xab\x77\xa0\xbc\x6d\xd8\x50\x52\xf9\x77\xd1\x07\x92\xf2\x17\x82\x2c\x0d\x39\x0d\x72\x3f\xdc\x34\xc8\x0d\x28\x20\x5f\x04\xaa\x45\x45\x94\x6f\x0c\x15\xaf\x0d\x93\x50\x35\x55\xdb\x60\x25\xf5\x63\x18\xc3\xcf\xa7\x6a\xc9\x59\x75\xd5\x2d\xba\xe0\xe5\x2d\x3b\xb0\x46\x0f\x8a\x8b\xbe\x34\xfc\xf3\x56\x80\x9f\x19\x4b\x2d\xc4\xc5\xca\xcb\x46\x86\xe8\xb9\xc9\xf2\x11\xd1\x82\x2a\x57\x91\x0a\x5e\xb5\xca\x52\xb2\x2b\xb6\x5c\xd6\xe0\xa8\x43\x3a\xca\x50\xcd\xda\xf2\x41\xb9\xf4\xe9\x81\xd2\xa4\x27\x33\x1b\x2c\xb5\x52\xd0\xf2\x26\x4b\x16\x9d\x0a\x86\xb3\x9c\x2f\xe3\xa0\x88\xce\xe8\x4f\x41\x7e\x9c\xc3\x9b\xbe\xb2\xaa\x1c\x58\xab\xae\x69\x6d\x0d\x53\xa3\x1c\x1a\x3b\x9d\x4c\xe8\x58\xd4\xcc\x57\x6e\xc9\x72\x28\x2f\xe0\xa3\xe7\x52\x68\x7b\x51\x9a\xd6\x22\xb2\x58\x9c\x4e\x6d\xe3\x4b\x9d\x81\x22\x0a\x39\xda\x41\x50\xf1\x79\x75\x83\x9e\x07\xca\x0c\xb6\x4e\x11\x28\x5a\x6a\xb4\x0e\x81\xc8\x9a\xaf\x3c\x38\x38\x55\x2e\x36\xa8\xb0\xc1\x52\x33\x6b\xc2\x26\x46\x50\x83\xf6\xd9\x4d\x94\x73\x26\xf0\x52\x28\xf4\x0f\xf8\x68\xd8\x1f\x05\x39\xad\xe5\x8d\x3e\x50\x1f\x19\x78\xe0\x0c\x02\xe0\xf9\xd3\x20\x7f\x1d\xcd\xa3\xc2\x43\xbf\x26\x80\x28\xab\x12\x4b\x88\xde\xc8\x37\xca\xe4\xd1\xaf\xbe\xdd\x4e\x67\x1a\xd0\x45\x34\xa7\x79\x11\xcc\x17\xa5\x45\x14\x84\x5e\x58\x3c\x23\x29\x63\x5b\x46\x76\x59\xb5\x4a\xa7\x82\x3a\x13\x46\x93\x49\x34\x5e\xc6\xf0\xae\xa7\x0c\xd3\x1a\xc8\x1c\x48\x5a\x04\xf1\x8b\x26\x15\x58\x90\x58\x6a\x36\x57\xaa\x00\xd7\x3c\xce\x5c\xaf\x6e\xb6\x2b\x6b\x46\x05\x9d\x77\xed\x17\x7d\x8e\x59\x25\x40\xb9\x17\xd8\xc6\xaa\xf6\x49\x6d\xbc\x60\xdd\xf2\x1e\x71\x9d\x4c\x83\xc5\x1d\xa7\x53\xef\x2a\xc6\x1c\xc5\xb7\x86\xe3\x74\xaa\xd5\x6f\xee\x42\x86\x7a\x8d\xc5\x8c\x2b\xc4\x4b\x19\x5d\x7b\x44\x13\xf6\x65\x6c\x6a\x6a\x9c\x56\x86\x87\xc6\xec\xa2\xbb\xb8\x4e\x67\xd7\x32\x2a\x6e\xb0\xfd\x79\x2b\x31\x9a\x88\xd3\xa9\xa7\x6a\x99\x5a\x52\xa5\x2a\x64\x9e\x2e\xe0\x2a\xa7\xfe\xc4\x7c\x3e\x8b\x72\xc6\x8d\x17\x69\x5e\xdc\xe0\xc8\xfc\x2e\xcd\xab\xa5\x22\x37\x06\x53\x25\xd7\x76\x2b\xc5\x13\xcd\x3a\xa9\xcc\x42\x07\x03\xe8\x74\x7f\x11\x5c\xc2\xbb\x8a\x3d\x43\x4d\x86\xb3\x04\x92\x21\xa9\x28\x62\xef\x79\x4d\x66\x62\xd8\xf3\x34\xfb\xfc\x31\x7d\x97\xa5\x67\xb4\xbc\x0c\x02\xc2\x65\x17\x42\xe2\x2d\x2f\x28\x21\x50\x68\x81\x09\x8e\x3c\x65\x58\x52\x73\xd6\xc2\x3b\xc9\xdd\xac\x60\x9e\x82\xd2\xc9\x9e\xf1\xf5\x8c\x9c\xa0\xcf\x53\x32\x54\x66\x0c\xd7\xba\x55\xae\x83\xe7\xea\xf8\x38\x4e\xcf\xe1\x59\x89\xd4\x6b\x54\x55\x5f\xfd\x0c\x82\xc7\x4e\x64\xc4\x44\xd2\x24\xbe\xe4\x01\x21\x0a\xe3\x75\x86\x7c\x21\xc1\x5f\x42\xf8\x1e\xf6\xc8\x67\x12\x64\x68\x3f\xda\xc1\x0f\x24\xec\xa3\x35\xeb\x63\x23\xde\xa5\xee\x83\x80\xfe\x85\x95\xaa\x97\x9b\xd5\x51\xba\x9f\xac\xcd\x7b\x85\x6a\xc2\x16\x74\x0d\xf8\xa5\x17\x8b\x28\xbb\xf4\xac\x78\x94\x8b\xc9\x2d\xe7\xee\x63\xbc\xd0\x2c\xaf\x6c\x09\x58\xa0\x9e\x05\x00\x94\xed\x93\x25\x2c\x88\xee\xae\x6f\x55\xbe\x0f\xce\x25\xc9\x88\x14\x2f\x18\xaa\x7e\x3f\x1f\x47\x91\xbd\x7c\x65\x19\xbc\xdb\xfe\x3d\x17\x88\x53\x70\x48\x9a\xd3\xeb\x50\x35\x00\xfe\x94\x21\x0a\x9a\x8f\x39\x0c\x06\xab\xac\x08\x58\x9b\x78\x35\x96\x2e\x46\xbd\xdc\x6e\xb1\x92\xac\x6b\x00\x8e\xa2\x66\xf4\xaf\x98\xaa\xad\x91\xf3\x85\x4b\xc1\x66\x3e\x11\xbf\xc6\x4b\xe8\x39\xdc\xe8\x75\xcc\x60\xda\x70\xd5\x31\x0a\x92\x7e\x94\xff\x23\x88\xa3\xb0\x03\xb1\x2e\x44\xca\x8b\x28\xa3\xe3\xa2\xe3\xbb\xe7\x10\x2e\xc5\x00\x50\xd4\xd8\xe9\x3a\x97\x28\x58\xd0\xd1\x31\x88\x64\x0f\x3c\xd5\x1a\x5e\xeb\x3c\x15\x35\xa8\x42\xf4\xcc\xac\x89\xab\x27\x6c\x1b\x12\xe1\x57\x5c\xc2\xb6\x65\x50\x70\xbd\xd0\x3f\x5c\x26\xe3\x28\xf1\x4b\x2b\xc2\x91\x37\xba\x79\x5a\x37\x93\x88\xeb\xd7\xc8\x10\xcf\xc0\x0b\x12\x18\x21\x46\xc9\x14\x0e\x38\xde\xe3\xad\x0b\x66\xfa\x92\x12\x6e\x9d\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\xf3\x2b\x88\x72\xcb\x1b\xc0\x55\xec\xda\x35\x2c\x96\x71\x4c\xc3\xba\x2a\x30\x54\xc9\x51\x57\xbb\x97\x2a\x89\x20\x50\x77\x81\x3a\xac\x85\xe8\xe9\x7a\x2a\x2a\xa8\x29\x69\xdc\xad\x0d\x3d\x69\x08\xd6\x77\x36\x18\x96\x67\xa1\x92\x36\x2f\x19\xfa\x93\x51\x09\x63\xe7\x1b\x7a\xd2\x38\x6c\xd9\xf5\xfb\xb0\x34\x07\x97\xf3\x0f\xa8\x3c\xaf\xa4\xac\xad\x93\xf3\x54\x61\x83\x18\xbd\x37\x54\x0e\x43\x6f\x2a\x86\xc7\xa7\x9a\xa1\x27\x0d\xc3\x5a\x68\xf4\x24\x62\x68\x9b\x0b\x0d\x4b\xd2\x39\xf7\x32\x6c\xb6\xf8\x05\x53\x6b\xb8\xf5\xb4\xcc\x13\x11\x63\xe1\xad\xe1\xce\xce\xf5\x69\x6f\x67\xeb\xde\x8b\xc5\xbd\xd1\xd6\x7f\x8d\xd1\x96\xa0\xf4\xbb\x08\x47\xb3\x9a\xef\xfe\x86\x96\x5a\x3c\x5a\x8e\x69\x82\xc5\xd3\xbe\x42\x10\x80\xe6\x6e\xfb\x83\x38\x1e\x58\x91\x2d\xe1\x3d\xae\x1d\x16\xc7\x75\xe6\x2f\x8d\xca\xdd\x08\x60\x15\x4e\xfc\x7d\x21\xc0\x3e\xf1\xcd\x4d\x38\x99\xc7\xc1\x6f\x57\x77\x00\xaf\x2b\x15\xbb\x03\xae\x95\x27\xdd\xae\x5a\x88\x9b\x17\xc0\xb1\x0e\xea\x94\xdf\x18\x46\x06\xc7\x15\x20\xe2\x13\x43\xdc\x49\x00\x02\xb6\x3f\xd8\x93\x61\xb8\xaa\x04\xbb\x00\xfd\x84\x0b\x9f\x2c\xb2\x69\x8e\x65\xdd\x1b\x84\x7c\x96\x32\xbe\x0e\x8f\x07\x7e\x20\x80\xd7\xf3\x47\x47\xd9\x34\xe7\x51\x06\xd6\x85\xf0\xd6\xac\xc3\x58\x18\xab\xec\x34\xee\xde\x0f\x0e\x29\xc9\x1c\x1c\xec\x4f\xbc\x6e\x74\x07\xe7\x1f\x9b\xed\xbb\xa0\x42\x4c\xec\x68\x3c\x34\x44\x44\x55\x48\x3f\x1c\x85\xd8\x17\xb7\x2a\xca\xc9\x38\xcd\x32\xd7\xa5\x24\x9c\x80\x82\x82\xee\x67\xd3\xdc\x17\xe5\x4f\xc7\x19\x7f\x48\xfe\x06\x27\xa8\x9c\x7c\x81\xf3\xd3\x35\x6b\x2f\x2a\xc4\x9b\x0e\xc3\xeb\xa4\x67\xaa\x70\x3b\xa5\x73\xa4\x0f\xb9\x1c\x0a\x50\xe4\xd8\x82\x04\x1a\xf1\xec\x8c\xcf\x1f\x02\x80\x42\xc8\x70\xe7\x02\x9b\x27\x38\x11\xd4\xd1\xbb\xd8\x56\x1b\xc0\xd3\xbf\x2c\xb8\xcc\xd7\x8c\xb9\x5b\xef\x38\xc1\x1f\x83\xae\x72\x49\xce\xce\xc5\x8e\x8a\xde\xba\x31\x10\xe0\xdc\xbd\x70\x25\xbc\xbe\x94\x30\xca\x58\x05\xac\x57\xb4\x70\x56\x97\xd8\x91\x84\xb8\xbe\xb7\x57\x46\xc8\xe6\xcb\x25\x76\xf6\x15\xf1\xd7\x2a\xa2\xa4\x75\x1c\x87\x02\x55\x2e\x78\xa5\x52\x06\x6c\x94\x30\xa9\x18\x41\x24\xd2\x77\x1c\xcc\x43\x5e\xce\xa6\xa1\x5d\x6f\x97\xb8\xe3\x0d\x62\xd5\xaa\x36\xd9\xaa\xa4\x3c\xd5\x7e\x25\xd9\x19\x61\x47\x57\x67\x18\xab\xf2\x0b\x33\x5a\x68\x49\x38\xd2\x6b\xcd\xcd\xf1\xf2\xe9\x78\x62\x83\x16\xa9\xdf\xf3\xbf\x11\x3b\x74\x8f\x94\x78\xf5\xf7\x39\x87\x17\xef\x56\xd0\x70\x8d\xa0\xa4\x15\xb6\x4f\x25\x51\x6b\x24\xea\x6f\x16\x9d\xc3\x5b\xbc\x72\xde\x6f\x14\xa3\x43\xb8\x17\xdf\xec\x91\xa7\x52\x1b\x54\xd1\xc4\x32\x59\x04\xe3\xcf\x47\x5c\x0f\x6d\x58\x01\x42\x92\xa1\x1b\x32\x93\x74\x17\x4c\x37\x4b\xb2\x2a\xfe\x43\x91\xde\x1e\xd9\x26\xcf\x64\xa2\xf4\x80\x4e\xe4\x39\x50\xbb\x04\x50\x7e\xcb\xcb\x1c\xa0\x63\x21\xa7\x27\x8a\x9b\x33\x2a\x74\x29\xd8\x7d\xb3\x8a\x7d\x77\xb2\x79\x4a\x86\x3e\x27\xdd\x07\x10\xfb\x39\x40\xe1\xb6\x25\xb2\xec\x80\xde\x41\x1c\xe3\xc5\xdd\xef\xf7\xe5\xfa\x3e\xb0\xcb\x5a\x9b\x8f\xe3\x1e\xe7\x90\x6f\x77\x10\xd6\x57\x82\xb2\xdd\x28\x50\x35\xf4\xcc\xb8\xfd\x32\x99\xfb\x82\x83\xb7\x87\xf2\xd0\x15\x18\xaf\xc3\x82\x24\x34\x7d\xa8\x48\x30\x1e\xf8\x9a\x9f\x8c\x58\x1d\x3c\x68\x20\x03\x17\x68\xf3\xd2\xae\x98\x55\x88\xdb\x5b\x47\xb5\xd0\xab\xb2\xd8\xc8\xab\x04\x3e\xf6\xef\x9b\x52\x06\xb3\xac\x25\xd5\x1e\x03\x07\x19\x2d\xff\x09\x97\xc9\x86\x58\x88\xd9\x0f\xb8\x7d\x36\xa5\x2f\x5c\x04\x8b\x3f\x76\x31\xad\xd0\xe7\xae\x9a\x25\x97\x96\x70\xfa\x5a\x7e\xdd\xf7\x14\x57\xeb\x68\x15\xe3\xa3\xc5\x8c\x23\x41\x54\xdd\x33\xba\xe6\x3e\xbc\x83\x52\x78\x09\x77\x8c\xf5\x80\x9c\x8d\x3b\x4f\x65\x9b\x34\xd8\x73\x1d\xdc\xb8\x3c\x00\xb9\xb7\x91\x2f\x2a\x0c\x27\x08\x3d\x6e\x5c\xb1\x6b\x3a\x15\xe6\x9d\xa6\xa1\xe3\x40\xbd\xc8\x2e\xad\x77\x83\x08\x14\x9e\x0a\x96\x8f\x97\x18\x6f\x1b\xc7\xf0\xb8\xbc\xe3\xb8\xa8\xe1\x14\xbf\x47\xa8\xd7\x39\x8e\xdd\x79\xd9\x3a\x92\x64\x2a\x37\x8a\x26\xe7\x4a\x7b\xdb\x30\x8b\xd4\xee\x0a\x56\x0b\x7f\xaa\xa5\x56\xbb\x66\x24\x49\x09\x40\x61\x8e\xf9\x03\xd9\x84\x43\x8d\x71\xd6\x74\xa5\x43\x1c\x31\x34\x48\xf8\x3b\xed\x24\x14\xbe\x1c\x21\xe4\x6b\xf2\x48\x1e\x54\x9d\xd8\xb7\x35\xcb\xd5\x88\xf7\xc6\xd6\x8d\x35\x0f\x1d\xf3\x16\x4f\x54\x57\x0b\xde\xdc\xd1\x3e\xcd\x8b\x68\x1e\x14\xf4\xa7\x00\x14\x88\x75\x54\x85\xc0\xeb\x28\x0a\xd7\x7c\x17\xd4\xf4\xf5\xa9\xa3\xd9\x0c\xa1\x71\xd5\xcd\x8e\x07\xb4\x6c\x66\xde\xcb\x66\xa8\x0c\x1d\x06\x21\x50\xa4\x2e\x50\xc8\x07\x78\x2a\xa6\xb4\x78\x61\x87\xf6\x91\x3b\xab\x5d\x4d\xdd\x5c\x89\xba\xee\x78\x9e\x1a\x21\x5e\xde\xe8\x8a\x95\x59\x11\xa9\xdf\x2b\x35\xdf\x22\x00\x21\x2e\x2a\xf1\x8c\xc8\xbe\x12\x61\xbf\x6d\x34\x42\x55\xff\x8d\x02\x12\xaa\x42\xab\x0e\xf2\x6b\x46\x27\xd4\x3a\x1a\x36\xc0\x6c\x31\x96\x6e\xaf\x72\x7e\x6a\xae\x63\x44\x02\xba\xdc\x2a\xa5\x62\x5c\xa2\xec\x1f\x9b\x2b\x11\x23\x4a\x8b\x04\xc3\x62\x8a\x11\x6c\x04\xcf\x89\xeb\x27\xce\xd2\xb8\x3e\x03\xe7\xb3\x9f\x58\x8f\xdb\x64\xc8\x3f\xac\x9d\xa4\xdd\x73\x84\x97\xa1\xf6\xcf\xa6\xf2\x94\x67\x39\x31\x9c\x53\x9d\xc5\x3b\x2e\xfd\x96\x72\x06\x59\x4b\x0c\x32\xac\x4a\xd9\xf6\xa3\x02\x16\x55\x6f\x3d\x9e\xd8\x42\x78\x82\x0b\x43\xd0\x59\x37\xb1\xa3\xad\x71\x60\x9b\x2f\xb0\x0c\x25\x7d\x73\xe8\xb4\xb2\xad\xc2\x42\x67\x3f\x58\x2c\xe2\x4b\xe1\x29\xa8\x11\x61\x75\x6d\x33\x36\xbe\x05\x58\xcd\xb0\xc4\x1b\xd5\x5d\x33\x0f\x22\xfe\x8e\x66\x3c\x3a\x04\xcf\xad\x63\xef\x78\x26\xec\x6b\x85\xdf\x91\xe9\x7a\xc5\x63\x57\x49\xa5\xe0\xe2\xb0\xa9\x31\x5c\x06\xe8\x4a\xcd\xde\xc9\x2f\x2b\x6e\x8a\x48\x7c\x24\x3a\xa9\xb4\x98\xde\xad\xa5\x8b\x1f\xf6\xf9\xa7\x8c\x3d\x24\xcb\x02\x81\x47\xd9\x78\x19\x07\xd9\xfa\xfa\xfa\x7a\x75\xc4\x21\x49\x41\xbb\x77\x12\x73\x88\x6b\x7f\x5b\xc3\xed\x27\x7e\x07\x2e\xdb\xf7\xb7\xff\xf7\xb7\xff\x7f\xed\xdb\x7f\x71\xf5\xcf\x60\x65\x4c\x28\x7f\x24\x8b\xdf\x2d\x46\x85\xcf\xb2\xa0\xda\x10\x60\x6d\x30\x80\x98\x57\x41\xc6\x48\x99\xed\x60\xcb\xdc\x1c\x22\x23\xb8\x30\x9a\x4c\x68\x46\x93\x82\xd0\xe4\x2c\x87\x42\xa3\x2c\x3d\xcf\x69\xb6\x86\x1c\x7a\x9e\x47\x49\x98\x9e\x83\xc6\x02\x45\x7a\x20\x0f\x1e\x88\x9c\xfe\x3f\xdf\xbc\x7e\x55\x14\x0b\xe1\x2b\x96\x73\x4d\x33\x8d\xec\xf9\x61\x81\xf5\x89\x40\x05\xd1\x34\x49\x19\x23\x88\xa3\x84\xb2\x9e\x24\x69\x48\xd7\x90\x77\x30\xa7\x46\x35\xf0\x8b\x79\xcc\x46\x26\x36\xb6\x76\xb7\x69\x23\xd7\x1c\x93\xff\x7c\xf5\x7e\xdb\xa8\x6e\x96\x6d\xb7\xbb\xa5\xa5\xa4\xe4\xc0\x5a\x78\x27\x91\xe9\x9a\x44\x80\xfc\xc4\x44\x7b\x70\x8f\xc9\x9d\x69\xb3\x5e\x2a\x03\x08\xa3\x3c\xde\xf2\x67\x69\x5e\xf4\x48\x11\xcd\x69\xba\x2c\x7a\xac\xc2\xac\x07\x4a\xe6\xf3\x34\x13\x8f\xd1\x60\x33\x61\x70\x64\x8f\xc0\x7f\x57\x57\xa4\x2d\x88\x3d\x4e\xc7\x41\xcc\x12\x87\x4f\xbf\x79\xfc\x0d\x04\x96\xe5\x7b\x0f\xaf\x90\xed\x84\xe2\xd7\xd5\x15\xd9\x54\xd9\xac\x19\xb2\x07\xad\xa9\x34\xd9\x28\xd9\x53\xed\xd7\x0a\x4f\x8b\x8c\x2e\x20\x52\x1b\x3d\xb7\xa6\xcc\x92\x9d\x04\xe0\x7b\x74\x96\x11\x92\xd3\xf3\x34\x8d\x69\x90\x5c\xc3\x1d\x2b\xdb\x9f\xa5\x04\xa3\xb1\x2c\xdc\x32\xa2\x03\x9f\xd9\x96\xe1\xfb\x07\x63\x1a\xc9\x5d\x66\x07\xcc\x8b\x40\x56\x3d\x47\x35\xbf\x41\xe1\x84\xc4\x78\x18\xdc\x00\xea\x6c\x42\xb4\x78\x05\x43\x7e\xf5\x7e\x5b\xc7\x75\xe5\x92\x16\xc2\x3c\x9a\x08\x06\x63\x38\xbf\xb3\x2a\x32\xc6\xc3\xab\x04\x79\x58\xd6\x9a\x2e\x68\xd2\x69\xbf\x3b\xfa\xf0\x51\x86\xa2\xe4\x84\xc3\x3b\xb7\xbb\x86\x3c\xe9\xc1\xdc\x3e\x78\x60\x4e\xaa\x71\xe8\x5b\x82\x41\x4d\xfb\x79\x90\x47\x63\xd2\x26\x1b\xd0\x85\xe7\x4b\xc6\x1e\x50\x15\x1b\xa4\x3d\x54\x57\x85\xaa\x9e\x7e\x91\x8a\x47\x6b\xed\x51\x90\xd3\x27\x8f\xdb\xd6\xf8\xb5\x1f\xe9\x57\x34\x08\x69\xd6\x69\xef\x03\x5f\x8d\x7e\x0d\xf8\x69\x0b\xda\xe7\x23\xac\x28\xc4\xe4\x63\x9a\x14\x8f\xd8\x41\xbb\xdd\x23\x6d\x26\xf9\x47\x63\xa8\x62\xf0\x4b\x2e\xd5\x8e\xea\xc6\x4a\x4c\x59\x0d\xb9\xf2\x88\x23\x97\xc9\x18\x1d\xaa\x6d\x4d\xb2\xef\xe2\x79\x81\xae\xaf\xfd\xb1\xa5\xab\x48\x2f\xb7\x63\x0d\x4a\x5d\x9a\x4d\x72\x92\x66\x4c\x5a\x15\xc1\x8a\x81\x1e\xb5\x76\x5f\x63\x2e\x09\x3b\xf0\x20\x82\x47\x91\x89\x26\x97\xaa\x7e\x81\x64\xa9\xc8\xc7\x6e\xa2\x7d\xd6\x00\x07\x69\x92\x50\xf1\x6c\x41\x52\x98\xa6\x44\xe3\x72\x51\xb6\x2e\x03\x36\x7c\xa4\x17\x85\xd3\x41\x01\x8b\x5e\x6b\x08\xeb\x78\xb3\x5b\x55\x5d\x7a\x2f\xea\xef\xf8\x1a\xc4\xab\xa4\x79\xec\x60\xa0\x81\xa0\x86\x08\xf6\x15\xc7\xa9\xa0\x04\x91\xf5\xa3\x13\xad\x83\x14\x59\x34\x9d\xd2\x8c\x87\x18\x62\xb3\x0f\x62\x8b\xf2\x17\xca\x70\x50\x47\x30\xd0\x03\x1f\xd5\x98\x91\x82\x9b\xd0\x0f\x18\xaf\xec\x1a\xdc\x24\x01\xdf\xce\x79\x11\x14\x74\x3c\x0b\x92\xa9\x5f\x81\xc0\xcd\xfb\x25\xe2\x83\xf0\x12\x0c\xeb\xe1\x46\xf8\x31\xe3\x30\x36\xcb\x5b\x37\x23\xfd\x36\xa0\x18\x0d\x28\x6f\x95\x50\x08\x29\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\x6d\x29\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\x23\x76\x4e\x3b\x8f\xd8\xd9\x23\x80\xa8\x60\xe5\xab\x09\xb5\xe9\x99\x0b\x8d\x4f\xa5\xcf\x50\xd1\x27\xa2\x70\x48\xbe\x53\x7f\x52\xdf\xc7\x76\x9f\x6c\x31\x1e\x92\xf6\x76\x7f\x53\x29\x0f\xa5\xfe\xb1\x9d\xd0\xe2\x53\x1c\xe5\x05\x4d\xa2\x64\xaa\xb2\x95\xf6\xf0\xd4\x30\xe8\x92\x0a\xae\x8c\x87\x38\x73\xc9\x57\x5a\x15\xb2\x41\xea\x49\x70\xd4\x05\x78\xe8\x52\x55\x60\x9c\xf6\x99\x98\xdb\x1a\x3e\x65\xbf\x0c\xf9\xb9\x35\xdc\xfa\x96\x9d\xfc\x77\xee\x4f\xfe\xf7\x27\xff\xbf\xf8\xc9\x5f\x1b\xfe\xc3\xa3\xc5\x3b\x32\xfa\x57\x86\x9c\xf8\x54\x39\x8a\xa6\xdc\x06\xb7\xff\x0b\x3f\xa1\xf3\x7b\x90\xf0\x35\x9d\x98\x1b\x82\x8a\xf5\x78\x89\x1e\xce\x19\x1b\x27\x87\xe0\xec\xe2\x7c\xc6\x7a\xdf\x31\x0d\xb4\xbe\xe7\x85\xc9\x43\xb2\xed\xbe\xbc\x03\x8b\x3f\x26\xc5\x9b\xef\x0f\x89\xff\x45\x9c\x60\xee\xef\xc4\xa9\x2e\x48\xc8\xe1\xf3\xfd\xb7\x62\x92\x43\xf2\xdd\xb7\x64\x9c\xce\x17\x4b\x11\x67\x65\x74\x49\xe6\xe9\x59\x94\x4c\x51\x34\xb1\xc7\x64\x3c\x0b\x32\xd8\x0b\xf8\xcd\x6c\xc8\x4d\xa9\xa4\xb9\xba\x84\x8e\x29\x7f\xb4\x50\xa4\xac\x41\x8e\xab\x9c\x74\xf6\xc9\x1e\xd9\xda\xec\x91\xe7\xec\xff\xad\x1e\xe9\xf7\xfb\x3d\xf2\x7f\x64\x8f\xec\x7c\xd3\x65\x87\x1d\x92\x2f\xe8\x38\x9a\x44\x7c\x21\x1d\x7e\x38\xda\xda\x79\xb2\xf5\xc4\x36\x31\x8b\xf2\x14\xd2\xc5\x38\x5c\xaf\xb2\xd7\xfc\x4d\x2c\xeb\x08\x1b\xa0\x79\xb5\x86\x6f\x96\x85\x24\x15\x4a\x30\xe1\xda\xc0\xac\xdf\x98\x50\x56\x31\x9e\x47\x36\xa2\xf6\x7e\xbb\xcf\xd0\x72\x90\x86\x74\xbf\xe8\x6c\x22\xad\x35\x1b\x5b\xfb\xff\x9c\x6c\xce\x00\xf9\xbb\x5d\x20\xd6\x22\x3d\x5e\x2c\x68\x76\x10\xe4\x5a\x95\x8d\xb2\xf3\xe5\x28\x2f\xb2\xce\xe3\xae\x7c\x91\x2b\x12\x36\x7b\x8f\xad\x1b\x33\x9e\xbb\x88\xa3\xa2\xd3\x6e\x77\xcd\xc7\xca\x49\xd7\xb4\xae\x1a\xa7\x21\x1b\x5c\xe2\xeb\xbc\x94\x0f\x01\xe6\x87\x3d\xb2\xcf\x04\x42\xf8\xf8\x7e\x8f\xfc\x5f\xd7\x89\x01\xe0\x99\x59\x31\xb1\x06\xa4\x72\x2a\x1b\x52\xf2\x88\xec\x93\x0d\xb2\xb5\x89\xec\x8c\x7c\x7e\xf1\x65\xec\x51\xdb\x86\xe9\xba\xdb\xff\x25\x8d\x12\x36\x4c\xdb\x52\x71\xbc\x04\x8f\xbb\x30\xc5\x6f\x8e\x5e\x30\xc2\xde\xda\x94\x4c\x49\x58\xf8\x01\xe5\x7b\x28\xee\xdb\xcd\x27\x8f\x6d\x82\x9b\xa7\xe1\x77\xdf\x6e\x6d\x96\x11\x9a\x49\x5f\xda\x8f\x31\xa7\x26\x51\xb8\x92\x8a\x32\x3a\x0f\xa2\x84\xeb\x8e\x58\x9e\xbe\x7b\x14\xce\x5d\x4c\xf6\x20\x80\xb5\xdd\xf2\x76\xd7\x72\x5b\x03\xcc\x4a\x82\x29\x8b\xd7\xef\x0c\x13\x39\xdd\x24\xc8\xda\x87\x49\xc1\x3d\xe2\xf4\xc8\xd6\x66\x97\xfc\xff\x19\xd6\x36\x9c\x5a\xb8\x53\x1c\x61\x7e\xee\x7b\x81\xab\xea\x52\x25\x75\x7d\xc6\x3c\xd5\xbf\x43\xe2\x26\xe8\xb0\x0e\x84\xc1\x3f\x5c\xa8\x43\x82\x78\xeb\x20\xd8\xa7\x9c\x2f\xff\xe4\x0c\xb0\xb7\x6b\xff\x24\x08\x4b\x68\xbd\xe4\xdc\xae\x3a\x51\x66\xeb\xfa\x49\x21\x08\xd1\x72\x2e\x5f\xe7\x58\x44\xc5\x60\xf6\x55\x8e\xd3\xf7\x00\x65\x49\x31\x9a\x0d\xe1\x5a\xb1\x35\xac\x15\x63\x39\x7d\x54\x63\x9d\xcf\x80\x20\x7f\x2e\x7d\x06\xa0\x97\x0a\x22\x1a\x28\xd9\x7a\x82\x58\xd8\x28\xc8\xe9\xce\x13\xb2\x07\x65\xb4\x7a\x68\xe7\x89\x61\x02\x10\x86\x94\x6b\x16\x61\x0f\xec\xf0\x42\x3d\xb2\xf5\x8d\x29\x09\xab\x7e\x3e\x1f\x05\x49\x87\x17\x33\x99\x9f\xb5\x98\x85\x5b\x12\xb4\x70\x9f\xb3\xa1\x17\xa9\xb1\x7b\xb1\xe9\x23\xe0\x5a\x35\xbb\x94\x2b\x9a\x2b\x93\xc0\x5e\xf7\x1d\x8f\x05\x91\xa4\x85\x10\xca\xbe\x8f\x7e\x68\x4d\x41\x22\xe1\xee\x6e\x26\x1a\xa9\xf9\x2c\xe0\xd2\x1a\xec\x6f\x17\xe3\x78\x99\x47\x67\x2a\x74\x65\x34\x8a\xe2\xa8\x50\x02\xce\x28\x48\x3e\x0f\x46\x59\x90\x8c\x67\x24\xa7\xd9\x59\x34\x96\x1b\x60\xc0\x3d\xbd\xb6\xbe\x1f\x44\x3f\xf4\x6d\x1a\x52\x61\x24\x72\xb9\x0b\x4d\x68\xc6\xb6\xa1\x20\x9e\xa6\x59\x54\xcc\xe6\x24\xa4\xf9\x38\x8b\x46\x9c\x2d\x09\xf9\x87\x26\xfd\xf3\xe8\x73\xb4\xa0\x61\x14\x80\x10\xc4\xbe\x06\x87\x49\x41\xb3\x24\xe0\x4f\x27\x3e\x3d\x0f\x92\xcf\x9f\x84\x9b\xd9\x4f\x7c\x5e\xff\x7f\x3f\x89\x91\x26\xd3\x4f\x6c\x88\x9f\xe0\x2d\xd1\xa7\x30\x9a\x46\xce\x53\x0e\x39\x35\x3e\x8a\x1c\xc9\x3d\x55\xce\x80\xf4\x19\x53\xa4\x9e\x6d\xb6\x01\xad\x3e\xb7\x57\xe4\xc8\x62\x8b\x62\x46\x0f\xf8\x3e\xd5\xfe\xe7\xcb\xf6\xee\x9a\x97\x67\x0a\x1e\xdb\xb1\x76\xee\x0e\xae\x60\x83\xb4\x37\x41\x54\x82\x56\xb0\xb9\x0b\x43\xc7\x0b\x86\x0d\xb2\x47\x3a\x5c\x9c\xea\x7c\xf7\x94\x3c\xd2\x4d\x74\xe5\xb3\x81\x47\xdb\xd6\x7e\xab\xbc\x6e\x98\x4d\xa1\x3a\x45\x83\x35\x6a\x2b\xc1\x44\x10\xae\x80\xb0\x79\x00\xf1\x28\xc9\x8b\xa8\x58\x16\xd2\x59\x72\x14\xd2\xa4\x60\x9b\x96\xed\x78\x9f\xd7\x72\x98\x84\x51\x46\x4d\x03\x06\xf3\x8d\x4d\xde\x93\xb2\xac\x7a\x64\x03\xaf\xa6\x5a\xa8\xa5\x16\x34\xd5\xd2\x6d\xb5\x56\xe1\x45\x66\x4f\xbc\x0e\x94\xcd\x23\xb0\xc9\x19\xda\x2f\x3f\xbe\x62\xf3\x20\x5f\xb7\x60\x0c\xa0\x54\xd5\xb7\xae\xc5\xaf\xd3\x2a\x7e\x2d\x9f\xd2\x71\xe4\x8a\xe8\xdc\x51\xce\x5f\xca\x61\x3e\xee\xc8\x9d\xe0\x41\xa5\x54\xde\x54\x7b\x91\x47\xf1\x21\x15\x1e\xfc\x39\x1d\x6f\x49\x09\x9d\x87\xc8\x3f\x4b\xa5\x9c\x10\x61\x3f\x2f\x11\x27\x2b\x2c\xfc\x69\x27\x2f\xb5\xba\x72\x85\x05\xe8\x7a\xe9\xeb\x41\x3c\x66\x1d\x95\xc1\x3b\xaa\x1e\x49\x3d\x5a\x1b\x18\x1b\xd6\xd6\xb8\xa3\xb4\x28\x61\xf0\x9f\x7f\xbe\x3c\xd9\x7c\xf4\xdd\xe9\x97\xed\xeb\xce\xcb\x8f\xaf\xd8\xef\xfd\x47\xff\x77\xfa\x65\x6b\xe7\xfa\x4a\x7d\xec\x6c\xf6\x76\xb6\xae\xbb\xff\x33\xe8\x17\xa0\x04\x55\x1b\xb8\xf1\x2e\xaf\x8c\x31\x20\x70\xfe\x3c\x6f\x6b\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\xec\x3c\x86\x38\xff\xfe\xbc\x6f\x6e\x38\xec\x09\x89\x92\x92\x81\x1b\xdc\xe7\x6e\x86\xee\x65\x23\x8d\x06\xbf\xbd\xd9\xc8\x6a\x93\x8b\x94\x6c\xa4\xf9\x72\xce\x00\x8f\x73\x71\x7c\x98\xa7\xe1\xa3\xef\xbe\x7d\xb4\xb5\xa9\xb2\xe1\x8c\x0b\xbd\x1b\xa7\x31\xe9\x1c\x7e\x38\x1a\x1c\xbe\x3c\x20\xec\xdc\x30\xdc\xde\xdc\xdc\xe9\xda\x3c\x19\x55\xeb\x9e\x42\x51\xae\x33\x70\x99\xd7\x70\xd8\xe2\x4c\xb8\xdd\x23\xdb\xcd\x6c\x55\x31\x53\x35\xb6\x14\x42\xa7\x7d\xf2\xcf\xf7\x2f\x7f\x72\x1c\x09\xaa\x02\xfe\xd1\x94\xd6\xe8\x4e\x2a\x82\xac\x1b\x9e\x26\x80\x0e\x78\x99\x73\x86\xfc\x6d\x8f\x3c\xee\x92\x21\x69\xb7\x1b\x8d\x7b\x1c\x47\xf0\x90\x4c\x75\x10\x94\x4f\x51\x62\x8f\x8f\x61\xe1\xa7\xfd\x7f\x1c\xfd\xf8\xaf\xa3\xf7\xff\x6b\xcf\x2a\xd4\x51\x32\xa7\x76\xfd\xde\xc9\xe5\x40\xb7\x1e\xfb\xd6\xd6\xea\x23\x17\xab\xc9\x7f\x2e\x71\x0f\x1e\xee\xd0\x9c\x0a\x9c\xe1\x05\x9e\x73\x08\xbe\x77\x12\x83\xf3\x39\x20\x33\x0e\x1d\xee\x80\x1f\xa3\x43\x6c\xe9\x51\x46\x9e\x3f\xd4\x29\xc5\x38\xa1\xf2\x33\x8a\x79\x9e\xd9\x7a\xd2\xed\x91\xed\x4d\xe5\xe2\xcc\x90\xf2\x24\x7a\xad\x41\xca\xc2\xcd\x16\x68\x89\x57\xaa\x43\xc8\xe2\x4a\x7d\xac\x57\x6c\x0d\xcd\xcf\xeb\xd3\xde\xce\xe3\x7b\x35\xfe\xbd\x1a\xff\x2f\xae\xc6\x17\x2a\xfc\xc5\xb8\xda\x7e\xef\x16\x16\x77\x2d\x1d\xa2\xb0\xb5\xbb\x52\x68\xb5\x1a\x3b\x3d\xae\x67\x5a\x8c\xbd\x96\x60\x8b\xa0\x98\xf5\x48\x42\x0d\xeb\xef\x4f\xa0\xb9\x70\x1e\x9e\xca\xab\x6a\x1c\xdc\x59\x7a\x2d\x10\xf6\x3a\x60\xe3\xc3\xfe\xe3\xa9\x3a\x6b\xac\x6e\x78\x81\x2b\x16\x32\xa1\xf3\x85\x41\x0f\x75\x79\xe5\x8a\xd2\x2a\xd6\x4f\x93\x4e\x1b\x46\xd5\xc6\xc1\x50\xbb\x86\xfd\x74\x9e\x32\x26\xc6\xdf\x12\x1e\xbe\x3b\x20\xfa\x5e\x99\xbf\x30\x6c\xf7\x08\x45\xac\xf7\x13\x67\x83\xe2\xc2\xbb\x63\x3b\xc3\xf4\xf6\x20\x09\x71\xfb\xa8\xf9\xd2\xca\xc8\x9a\x7a\x63\xf0\xfa\xf0\xc3\xc7\x97\x6f\x61\x05\x1d\x1c\xbd\x7d\xfb\xf2\xe0\xe3\xe1\xd1\x5b\xf2\xfe\xe5\x87\x77\x47\x6f\x3f\xbc\xfc\x50\xda\x6a\x18\x14\x01\x6e\x96\x7d\xe3\xcd\x69\xf0\x50\x98\x11\xce\x83\x8b\x71\x3a\x5f\xc4\xf4\x22\x2a\x2e\x87\xe4\x09\x50\x96\xd5\x43\xd0\x85\x2a\x3b\x04\x56\x95\xde\x6f\xba\x9e\xc8\x35\xc2\xe6\xe0\x8b\x19\xc8\x1a\x0e\x7e\xa1\x6d\x3b\x21\xba\xc3\x03\x7c\x03\x7f\x09\xc9\xf9\x2c\x1a\xcf\xc8\x3c\x28\xc6\x33\x21\xbe\xf2\x4d\x88\x31\xb4\xd0\x28\xe7\x89\x5a\x00\x4d\xfb\x23\x5d\xc3\x75\x94\xd3\x5b\xb0\x40\xf0\xc7\xa1\x8d\x26\x9d\x4f\x7e\x42\x3e\x81\xb7\x71\x51\x78\xea\xba\x43\x57\x85\xd9\x58\x05\xd8\xae\x03\x65\xc7\x0c\x2f\x8d\xa5\x0b\xd5\x88\xbe\xdb\x15\x5d\x3b\x58\x9c\x44\x19\x35\x3c\x02\xd8\xe8\x2a\x1b\x0f\x1b\x8a\xa7\xf5\x0a\x70\x1d\xd8\x17\x9b\xb6\xe8\xbf\x90\xc6\xb4\xa0\x55\x35\xd8\x83\xb1\x71\x83\x5f\x61\xff\xcc\x76\x2d\x20\x44\x41\x10\xbc\x3e\x50\xee\x70\x5b\xa9\x84\x3b\xcb\x21\x29\x77\xb5\x1c\x15\xfd\xb5\x35\x29\x0c\x9a\x24\xbc\x66\xab\x3d\xe0\x45\x26\x13\xfe\x34\xcf\x43\xe2\x91\x59\x18\x7b\x56\xc5\xab\xca\x66\x83\x3d\x4b\x5e\xfb\x07\xf7\x6c\xae\x1d\xf4\xca\x25\xfe\xe2\xe5\xa3\x83\x57\xc7\x6f\xff\xf7\xe5\x7b\x55\x4f\x48\xc7\xb3\x65\xf2\x99\x86\xe2\x55\x09\x7f\x31\x2a\xfe\xfa\x19\x5d\xc4\xc1\x98\x76\x06\xff\xbe\x3e\xf9\x77\xf2\xef\xec\xf4\xd9\xbf\xbf\x0c\xa6\xbd\xf6\xf5\xd5\xa3\x47\x57\x5f\xda\x5d\x70\x2d\xfc\xc5\x0b\xff\xef\x53\x59\xe2\x44\x94\x39\x65\x85\x4e\x64\xa9\xd3\x13\x7f\x39\xbb\x94\x51\xa8\xa4\x8c\x6e\x0b\xb5\xa4\x1a\x42\x65\xc4\x35\x1f\xcb\x6e\x4b\x4e\x6a\x60\xc0\x5d\xb3\x80\x78\xc4\x5f\x06\x03\xb8\x03\xa5\xc2\x1d\x06\x78\xda\x80\x0a\xd6\x1c\xd2\x67\x79\x07\x2c\xcb\x5c\xb9\xc2\xef\x8c\x05\x43\x36\x08\x7f\xff\x6a\x88\xea\xea\xce\xda\xe2\x64\xae\x53\x03\x9f\x2d\x18\xf4\x1d\x95\x12\xd6\x34\xdc\x98\x66\xcd\x5d\x7c\xba\x33\x7b\x76\x67\xc4\xd0\xc1\xe7\xae\xb2\xa0\x06\xd7\x77\xc9\x98\xc6\xe0\x50\x5f\x3e\xe2\x34\xca\x8c\x63\x1a\x64\xd2\x84\xcb\x6a\x45\x24\x5b\x0b\xda\x0f\x04\xbe\x1a\x0a\x59\x91\x6f\x8f\x33\xcb\xdb\x7b\x1d\xfe\xab\xb4\xab\x14\x38\xc3\xf0\xd7\x3d\xb2\xb5\xb9\xb9\x49\x1e\xf2\xcb\x19\xcf\x5d\xab\xd7\xf1\x03\xbc\xdb\x03\xec\x48\x7c\x31\x0e\x92\x53\x41\x2f\x3c\x16\x8b\x78\xd7\xb7\x3a\xaa\xdc\x19\xb3\x48\x04\xc2\xfd\x08\xcb\x4a\xa7\xc3\x9c\x45\xf0\x80\xf0\xa6\xdd\x9e\xa5\xad\xc7\xe0\xc2\xf9\x0f\xe3\x91\x3f\x89\x2d\x34\x08\xc3\x1c\x47\x0a\x17\x56\x0e\xae\x34\xc6\xd5\xc3\xbd\x35\xbe\xe1\xca\x83\x81\x38\x6b\x47\xdc\x5d\xbd\xe0\x7a\xb0\x1b\xcb\x5b\x21\x95\x7a\x18\xf2\x52\x41\x96\x45\x67\x14\x33\xdc\x20\x54\xb3\x27\xdb\xab\xe0\xb0\x1e\x68\xc3\x5b\xbd\xdf\xa6\x14\xc9\x14\xf2\xb5\x7a\x14\x92\xab\x2b\xf9\x75\xb2\x79\xaa\xb6\x4c\xb8\xc2\xe6\x7d\xd3\xd0\x22\xc1\x2c\xc1\x13\xb1\x44\xe7\xdd\xbc\xc8\x9e\xea\x4d\x95\xc4\xcb\x40\xfb\xaa\x61\x59\xb7\xdc\xd5\xe4\x3a\xc2\x2b\x95\x9c\xcf\xa8\xf4\x3b\x10\x72\xb1\x1c\x4e\x5f\xa0\x71\x67\xfb\x7b\x88\xd0\x2c\x88\xb8\x02\xb5\xae\x7d\xa7\x3a\xda\x4f\xd2\xac\xc3\xf0\xf2\x99\x5e\xf2\x93\xa2\x6f\x00\xa6\x13\x98\x8e\x1f\xa8\x3f\x0b\xf2\xa3\xf3\xe4\x1d\x04\x5a\x2a\x2e\x21\x76\xa1\xc5\x05\x4a\xd0\xf3\x99\x5e\x9e\x96\xdb\x76\xb6\xd3\x84\x1c\xbe\x3b\x68\xdb\x41\xfc\x85\x6c\x51\x51\xa7\x63\x66\xa1\x97\xc9\x01\xf6\x41\x28\xdc\x8d\x13\x74\xdc\x88\x72\x92\x17\x11\x0f\x46\x12\x85\x88\xa8\xb1\x59\x68\x29\xc2\xfd\x76\x9c\x9d\xf2\xd3\x92\x94\x03\xd8\xee\x91\x51\xd1\x8f\x1e\xa7\x02\xb3\x57\xd3\x34\xa1\x42\xf3\xd4\x59\xff\x64\x8b\xfd\xe7\x59\x54\x80\xbf\x14\x8b\x1b\x21\x10\xeb\x08\xf5\xc9\x3d\x43\x49\x17\x83\xeb\x65\xb5\x0b\x05\x92\x77\xe8\x55\x2f\x08\xd6\x30\xfd\x58\xf5\xd2\x0f\xe8\xe9\x0a\x31\x36\xd9\x5d\x83\x73\xaf\x80\x22\x89\xa6\x7a\x2c\x11\xcf\x11\xaa\xf6\xac\x29\x7b\x19\xa2\x67\xbf\xbe\x51\x55\x58\x3c\xdf\x4c\x6c\x50\x54\x8d\xa5\x06\x73\x28\xb5\xfb\x28\xb1\xfe\x7c\xfb\xa4\x65\x76\x27\xb4\x89\xd6\x19\xc5\x71\xc7\xf3\xaf\x74\x09\x56\xd6\xfa\xb5\x59\xab\xbd\x61\xb3\xdb\x8d\x76\x8b\xe4\xd8\x30\xbb\x8f\xed\xb4\x35\x1f\x84\x17\x5b\x69\x41\xf2\xe5\x62\x91\x66\x05\xe8\xd6\xf8\x4d\xed\xbb\x03\xa2\xb4\x2a\x6d\xc3\x11\x64\x39\x61\x36\x7e\xa9\x70\x93\xc5\x58\x4f\x65\x2b\x51\x98\xf7\x58\x0f\x34\x55\x69\x41\x8f\x1c\xea\xda\xbb\x69\xa9\xb7\x1b\x57\x8f\xab\x31\xe8\x38\x69\x2f\x79\xa5\x7d\x7d\xda\xdb\xf9\xe6\x5e\xa5\x7b\xaf\xd2\xfd\xaf\x50\xe9\x8a\x87\x15\xb7\x7a\x8e\xbd\x1f\x64\x69\x42\xfe\x77\x39\x0f\xce\xa2\x9c\x7c\x1f\xb0\xcf\xbf\x7d\xe6\x9f\xfd\x39\xf5\xaa\x7b\x07\x03\x72\x98\x44\x45\x14\xc4\xd1\xaf\x94\xfc\x9d\xf7\x82\x11\x6a\x40\x72\xb0\xc4\x92\x06\x37\x30\x50\xb6\x54\x0d\x27\xe7\x7d\xd0\xea\xca\x62\x32\x8a\x88\x08\x40\x75\x18\x0e\xc9\x66\xdd\xcd\x1b\xb7\xf6\x60\xc3\xb7\xdd\xea\x7a\xcd\x4c\xbc\xee\x74\xf5\x2b\x34\x19\xeb\x6a\x22\x11\x0a\x2d\x69\x83\x1e\x8f\x13\x5e\xfe\x3a\xa5\x87\x54\x3d\x13\x59\x8d\xcc\x92\xbe\x77\xbd\x6e\x88\xd0\x08\x58\x7b\x4e\xef\x07\x6b\x02\x3d\x25\xae\x78\x79\x5b\x3d\xd1\x98\xe1\x34\x95\x67\x75\xcb\x54\xcb\xb2\x49\xc7\x98\x47\x99\xed\xae\xb7\x51\xd4\xa9\x20\x3c\x63\x67\x54\x39\x3b\xe4\xf0\x05\xe4\xc8\xde\xa9\x49\xdb\xd8\x28\xf3\x33\xe4\x7f\xfd\xc3\xdf\x0a\x39\xd5\xe8\x6c\xf9\x3c\x48\x8c\x54\xa5\xcb\x77\x41\xfc\x7f\x76\x60\x92\x2f\x84\x9a\x1b\x5e\x48\x1c\xa8\xc3\xa3\x34\x20\xf2\x9b\xea\x28\x65\x5d\x5d\xe4\x33\xcf\xcb\x6c\xab\x01\xbf\x79\x86\x44\x83\xd5\x9e\x15\x10\x99\x27\x5a\x97\xa1\xdc\xa7\x0f\xd2\x39\x0b\xa0\x67\xaa\xed\x3e\x3d\xa3\xd9\x65\x47\x7a\x43\xfe\x10\x25\xd3\x98\xbe\xe1\x08\xef\x92\x21\xf1\x66\xe8\x9a\xc4\xb4\xaa\x8e\xf8\xc1\xc5\x04\xaa\x83\x96\x12\xde\x25\xdd\x20\x0b\x22\x99\xc6\x29\xd2\xb0\x2d\x12\x19\x72\x7e\xf6\xf6\xf6\x38\xd5\x60\x20\xe1\x76\x41\xc2\xb2\x33\x37\x03\xe3\xd7\xba\x6d\x5f\x75\x42\x86\xb5\x7c\x4a\x0e\x06\x3c\x34\x9f\x4a\x12\x5e\xd9\x31\x73\x91\xeb\xb1\x91\x3f\x79\xce\x88\x46\xf0\x1e\xad\x86\x1d\x3d\x67\x40\xe5\x2e\xbe\x45\xc7\x2d\xfe\xc2\xeb\xca\x39\x53\x15\x55\x49\x01\x27\xec\x82\xf2\x48\x2c\x8a\x8e\xe4\x3d\x5d\x32\x89\x68\x1c\x5a\xa6\x07\xa2\x15\xa3\xa7\x16\xcf\xc1\x1d\xb4\x18\x0f\xef\x9a\x45\x86\x32\xd9\x8a\xfa\x20\xc9\xc2\x75\x84\xe5\xb0\x37\x09\xdb\x97\xac\x4d\x7e\x0b\x16\x67\xea\xe1\x1d\x59\x51\xd4\x27\xe4\x44\x26\x06\x3e\xb9\x17\x03\xef\xc5\xc0\xbf\xb6\x18\xa8\xdf\xe7\xf1\x45\x73\x57\x2f\xf4\xee\xe6\xee\x9e\x81\xbc\x91\xea\xc6\x52\x63\x65\x38\x27\x8a\x48\x2d\xd2\x0a\x99\x7d\xa2\x53\xa4\x70\xb9\x26\x73\xd9\xa7\x71\x71\x0f\x3c\x4f\xe7\x6b\xc9\x60\x13\x81\x81\x4f\x7e\x1c\x94\x50\x1b\x42\xe3\x0c\x54\x82\x7b\x7a\xf6\x15\xb1\x72\x0c\xa5\x2b\x68\x0c\xde\x04\x49\x30\xa5\xfa\x75\x3e\x63\x59\x1c\x15\x86\x2a\x40\xba\xf0\xd0\xe0\x68\xbf\x9f\x1b\x18\x72\x2a\xce\xe6\x35\xf6\xef\x21\x65\x1c\x26\x4a\x4c\xff\x9e\x96\xf8\x37\x0a\x72\xee\x73\xa1\x2c\x12\xc5\x94\x82\x97\x4a\xcf\x26\x65\x7a\x9a\xb7\x1d\x8b\xca\x36\xcd\xf6\x80\xc4\x1c\x44\x88\x36\x4a\x63\x4d\x18\xee\x44\x51\xf8\x1c\x45\x1c\xca\x8e\x4f\xfa\x32\xcc\x99\x60\xa3\x52\xea\xdc\x1c\x73\x67\x9c\xfa\x92\x42\x84\xe6\x10\xdb\xae\x1a\x67\x9f\xbc\x61\xac\x3c\xa2\xb9\x08\x22\x0d\xf8\x70\xbc\x50\x1a\x9e\x3d\x1b\xe3\x4d\x0e\xea\xea\xed\x32\x8e\xb5\x63\x8c\x1e\x93\x22\xe9\x45\x04\xd7\x66\x3e\xdc\xfd\x31\xe3\x0f\xdd\x59\xd8\x1d\xb2\xf6\xb5\xe2\xee\x38\x98\x6c\x14\x6d\xc7\x0e\x70\xa2\x42\xc9\x98\x07\x31\x52\x13\x3e\xe6\xfd\xbb\x03\x11\x61\xa2\x3a\x76\x8c\x46\x9b\x70\xf5\xca\x09\x0f\x90\xae\x4e\x9c\x36\x9a\x38\xe8\x21\x83\x74\xb1\x64\x10\x9d\x4a\xf2\xa0\x03\xd5\x52\x89\x8d\x75\x0f\x77\x2d\xa1\x20\xdf\xe3\x46\x4f\x69\x4b\x86\x54\x4e\x17\x7b\x04\x82\x64\x57\x85\x90\x22\xcf\xf4\x6f\x4e\xdd\x50\xe4\x94\xb1\x03\xf4\x59\xe3\x59\xdf\xc1\x3a\xe7\xf7\x2a\x7a\x2d\xc6\xbc\x8b\x78\xee\x80\xb7\xfa\xac\x68\xba\x23\x2e\xc1\xbd\x27\x46\x8a\x19\x2c\x17\xa3\xd0\xde\xac\xc0\xd9\x0c\x1c\x7b\x9e\x79\x01\x54\x55\xde\xd8\x24\x02\x17\xbe\x90\x45\xf2\xfd\x94\xa4\xc3\x15\x22\x17\x05\x72\xdd\x36\x42\x42\xb3\x18\x44\xd8\x1d\xab\xd8\x47\x6c\x2f\xc9\x2b\x3b\x5f\x16\xf2\x04\x00\xa3\x65\x80\x01\x21\xcf\x08\x30\xa4\x8e\x29\x7e\x2d\x88\x54\x67\x80\x66\xa9\x44\x99\x51\xe5\x56\x19\xab\x38\x1c\x54\x49\x17\xb9\x1c\x9f\xa6\xb4\x35\xfa\x05\xa3\x8b\x65\xc8\xa1\x8d\x96\x51\x1c\x02\xc2\xc4\xa0\x58\xa6\xe3\xdf\x16\x18\xfe\xc7\xa3\x17\x47\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\x96\x11\xe9\x69\xf4\xcb\x09\xcb\x3e\xd9\x3c\x3d\x65\x62\x17\xfe\xbc\xba\x52\x76\x9b\x36\x28\xff\xb1\x05\x65\xd8\x58\x76\xfd\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\x03\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\x70\x74\x8b\x9b\x3f\x9f\xde\x47\x69\x6b\xb0\xe2\x87\x33\x27\xa5\xd1\x79\xf1\xdc\xe0\x23\x6c\x18\xa6\xcb\x0f\x47\x53\x01\x23\xb5\x92\x77\x2b\x02\x85\xad\x69\x79\x29\xef\x78\x6c\xfa\xc5\x05\x17\xf9\x42\x2c\xe9\xca\x92\x83\x3a\xac\x66\xb4\xb3\x08\x20\x47\xed\xd2\xf1\x75\xd0\xd2\x37\xeb\x5d\xbe\x3c\x60\xd1\x62\x59\xa8\xc7\x6b\x09\x3d\x17\xd8\xec\xe8\xed\x92\x09\x1d\x43\xd2\x56\x70\x56\x1c\x8d\x21\x69\x87\xa3\x4f\xbe\x5c\x29\x26\xee\xa8\x3e\xa9\x46\xa7\xb4\x59\xa3\x0a\xce\xdb\xa8\x2f\x57\x36\xba\xed\x36\xba\x58\x16\xaf\xe8\x45\xfd\x30\x5f\xd1\x8b\xb2\x31\x9a\x59\xd5\x03\xac\x6f\x8b\x03\x95\x0d\xcd\xdf\x96\x35\x2e\xb1\x19\x9d\x68\x38\x39\x11\x3d\x8d\xe4\x9e\x18\x7a\x4f\x74\x0b\x80\x4f\x4b\x76\xae\x17\xcf\xf5\xae\xc5\x69\xa7\x35\xdc\x81\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\xfc\xc8\x43\x81\x5b\xc8\x80\x44\x09\x72\x98\x7f\xb8\x4c\xc6\x7c\x8b\xc1\x50\x39\x4f\x95\x60\x8c\x15\x67\xd4\x06\x12\xa9\xaa\x2e\xee\xa0\x08\x43\x44\xa3\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\x6e\x9e\x76\xc9\x83\x07\x44\xfc\xee\x83\x4e\xf0\x68\xd2\x69\x6f\x5e\xb4\xb9\xeb\x92\xcd\x2e\x79\x46\x5a\xb4\x98\xb1\xdd\x03\x02\x93\x3e\xbf\x7c\x15\xe4\xb3\x16\x19\xda\xc9\x5c\xa3\xdb\xd2\x52\x02\x8a\xff\xf4\x63\x96\xce\x9f\xff\x06\x3d\x6d\x8b\x2e\xa1\xb0\x42\xcf\x2f\xa1\x61\xd6\xe9\xfd\x24\x3c\x64\xe5\x54\x34\x2f\x2f\x24\x1f\x87\x82\xd5\xe3\x59\x26\xe3\x98\xfe\x46\x03\x38\x66\x6d\xd5\x74\x1d\xc3\x94\x76\x5a\xce\x0f\x1a\xe7\x41\xba\x4c\x1a\x5d\x33\xdd\xc1\x38\xbc\x6d\x73\x12\xc2\x43\x29\x01\xe3\xa3\x72\xa6\xe0\x37\xec\xff\xb1\x6a\x10\x4d\x86\x33\x09\x18\xc0\xe8\xb3\xea\xde\xcb\x62\x76\xd7\x07\x84\xc6\x87\x83\x3b\x3a\x1b\x40\x00\xe0\xf2\xb3\x01\x57\x7d\x70\x2e\x1e\x51\x6f\x8f\x16\xb8\x33\x8b\x9a\x7e\x2c\x6e\xd0\x05\x74\xc7\xcd\xb9\x2b\xf7\x7f\x41\xb0\x87\xee\xc3\xe7\xfb\x6f\xad\x60\x64\x82\xa7\x72\xad\x0c\x7f\x40\x2b\x74\x33\xd7\x6b\x6b\xbc\x77\x7d\x6e\x19\xa5\xde\xd2\xbc\x2c\x66\x5a\x1b\xd4\x23\x6d\x1c\xba\xb9\xdd\x13\xc3\x9c\xd2\x62\x58\xa2\xf3\x94\xbe\x4a\xfb\xb8\xa0\x18\x49\x4f\xe8\xe9\x8c\xc2\x67\x41\x6c\x44\x19\xeb\x5b\x81\xb3\xcf\x82\xd8\x71\x46\xa2\xd2\xae\xd7\x00\x3d\x2b\x0d\x45\xf8\xf9\xbb\xc9\x60\x44\xd1\x9b\x0c\x47\x14\x6d\x38\xa0\x26\x67\x51\xc6\x5d\x82\x18\x2c\x37\x6b\x4f\x4e\x02\xd0\x3d\x3d\x49\x36\xe5\xe4\xab\x23\x14\xb2\xe6\x34\xae\xf0\x86\xe4\x44\x0b\x54\xfc\x7a\x4f\xb8\xd1\xfc\x51\xdf\xe6\xd9\x10\x38\xf2\x39\xe7\x27\x0a\x18\x85\x8e\xb4\xee\xb1\x86\xb8\x1a\x9e\xa7\x7c\xd6\x28\xa0\x92\x63\x73\x9a\x05\x53\xba\x5f\x34\x39\x39\x0b\xd0\x52\x1c\xf9\x20\xd4\xa1\xb6\x02\x4b\x7c\xdd\x71\x8e\x5d\xa4\x70\xb2\x5c\x05\x2d\xde\x81\x09\xe7\x8e\x35\x63\x62\x50\xa5\xc3\xb1\x32\x7f\xfb\xf9\xf6\x0e\x4c\xae\xfa\x3a\x7a\xe6\xec\xc8\x1a\x9a\x12\x19\x6f\x37\x2c\x5f\x6f\x7b\xce\x12\xd7\xf6\xaf\x6c\xf1\x92\xeb\xd5\xe8\x97\x35\x51\x4d\xbb\xb0\xff\xd6\x63\x02\xc0\x1c\x4c\x28\x89\xee\x6b\x60\x02\x91\xf2\x2d\x06\xdd\x5b\x2b\xa1\xec\xf9\x22\x8a\xf9\xe1\xad\x96\xbc\x05\x68\x05\x8d\xbb\x10\x12\x0f\x9b\xe5\xf4\x67\xcb\x6b\x0d\xe9\xd1\x2e\xe6\x74\xab\x4a\x64\x75\x3b\xb8\x75\xcb\x89\xaa\x9a\x1b\x39\x85\x2f\xe8\x38\x9a\x07\x71\x39\x2a\xb4\x14\xd8\x10\x09\xba\x40\x09\x51\xfe\x71\x07\x6c\x0a\x4f\x35\x83\x2d\x8f\x95\x5c\x72\x04\x03\xf9\xba\x72\xd0\xf5\x2b\x08\x55\x58\xcd\x3c\x3e\x7a\x4e\xa8\x2b\x8d\x49\x95\x72\x06\x57\x76\xf8\xfd\x23\x71\x9a\x9b\xe0\xe9\x3d\x1d\xd3\x68\xd1\x80\xcc\xdd\x32\x4d\x08\xc0\x05\xbd\x2d\x05\x88\x1a\x1b\x0f\xb0\xe1\x2a\xae\xe5\x62\x9e\xc1\xd9\x80\x4d\x28\x80\x8b\x45\x77\x24\x20\xd6\x2e\x6f\x76\x40\x7a\x1f\x9c\x37\x5f\xe2\x6e\x01\x3f\x22\x2a\xe1\x9a\x70\x36\x86\x07\x8f\x2c\xe4\x86\x96\xae\xeb\x6d\xa3\xae\xde\xbc\x9f\xf6\x4c\xf9\xd6\x98\x6f\x1c\xd1\x34\x59\x61\x1c\x26\x74\xc9\x38\x4a\x81\xbe\xf2\x38\x1a\x74\xbe\xbc\xc7\x77\x2e\x6b\x97\x10\x8e\x30\xee\xaa\xea\x28\x04\xfe\xf7\x76\xd4\xca\xb9\x49\x47\xd9\x5e\x70\x67\x27\x02\x33\x42\x7a\xd5\x98\x10\xa4\x7f\x68\x7e\x80\x9b\x50\x8c\x31\xc2\x5b\x71\xa5\x31\x97\x4f\x65\x5c\xf3\xba\x69\xe3\xd0\x7d\x19\xec\xbc\x64\x0a\xcd\x3a\x7d\x63\x2d\xed\xc8\xeb\xd7\xaf\x1b\xf6\x21\x2e\xa5\x20\x55\xd3\x4a\x2d\x7f\xa0\xd9\x82\xd6\x6e\x4f\x0a\x03\x1c\xba\x1a\x01\x0e\x4c\x45\x2f\xf2\xe5\x68\x1e\x15\x3f\xa7\x59\x9d\x94\xa4\x01\x4b\x56\xba\x2f\xbf\xfa\xea\xbb\x41\xab\x02\xaa\x74\x2b\x2e\x69\xcf\x3a\xe2\x38\xd7\xdf\x5a\xf1\xd3\xc3\x69\x4a\xd1\x61\xa4\x1e\xa4\xa1\x09\x06\x4b\xd8\x48\x01\xd9\xdf\x2a\x24\x0e\x60\x6e\x49\x5b\x7c\x70\x21\xf4\x51\xc2\xc8\x43\x05\xcb\xd2\x95\x60\x5a\x06\x20\x64\xa7\xb2\x6c\xab\x51\xd3\xa0\x17\x31\x12\x9d\xe8\x8a\x01\x28\xcf\xdc\xaf\xcc\x42\xa5\x25\x50\xf3\xe6\x8a\x76\x32\x5e\xbf\x7e\xed\x02\x73\xea\x47\x55\x2a\xc2\x34\x06\xcd\x12\xe0\x5b\x58\x38\xf0\x98\x6c\x4a\xd9\x5d\xe5\xa3\x59\xd3\x11\x23\x5d\xa5\x75\x35\x4d\x47\xd5\xc2\x8d\x92\x51\x90\x63\x45\x85\xe8\x00\x30\x4a\xb1\x6e\x05\x8c\x02\xb9\xee\xf6\x56\x68\x63\x1e\x25\xa6\x75\x8b\xd3\x82\x80\xb8\x61\xfd\xb3\x20\x9f\x65\x41\x51\x39\x86\x12\x98\x46\x3b\xc3\xea\x3d\x92\xb7\xb3\x15\x1d\xf2\x83\xd4\x9f\x33\xc4\x75\xb0\x79\xb8\x58\xbd\x87\xd3\x20\x7f\x97\x45\xe3\x4a\x9c\x95\xc0\xdc\x58\x09\xbc\x7a\x2f\x45\xd8\xa1\xbc\xaa\x97\x0a\xe6\x86\x6d\x8c\xd0\x15\x5a\x45\x33\xe5\x60\x5f\x89\x86\x64\x4c\x86\x7f\x70\x5b\x9b\xaa\xbe\xd9\xa0\xa8\x45\xcc\x42\x8c\x6b\x97\xfe\x58\xdb\x31\xa0\x2b\xcd\x51\x64\xbc\x58\x08\xc6\x45\x9a\x49\xf1\x47\x5a\x3e\x80\x19\x71\x8f\x30\x58\xc3\x96\x58\x40\xfb\x1a\x9b\x48\x4b\x07\xe7\x29\x6a\x0f\x3d\xb7\xe3\x50\x07\x19\x05\x4b\x25\x78\x3c\x76\x60\x99\xd3\xa3\x38\x45\xdc\x7a\x42\xd7\xc3\x50\x21\xc3\xcd\x5b\x37\x76\x3d\x69\x8e\xd1\xa7\xc5\xac\xd3\xed\xb9\x24\xfb\x3a\x9d\x22\xd9\xb8\x59\x97\x7c\x03\xd5\x86\x18\xd5\x2e\xf4\x25\xf6\x3b\xa2\x40\x7f\x1a\xa7\xa3\x20\xee\x33\xa4\xf6\x03\x37\x59\xc4\x3c\xf3\x35\x19\x8d\x83\xc5\xdb\x9b\x36\xcb\x0a\x3b\x8d\xf2\xc4\xaa\x26\x91\x55\x8a\x6e\xd0\x7e\xfa\x80\x63\x4a\xc9\x02\x1d\xff\xf4\xd4\x79\xa3\x7a\x59\xcc\xb4\x3d\x9f\x65\x98\xd3\x1a\x6e\x3d\xed\xb5\x1c\x03\x21\x61\xa0\xae\x2d\x73\x5a\xc3\xed\x6f\x20\x81\xcf\x69\x6b\xb8\xfd\x1d\xff\x54\xb4\xd0\x1a\xee\xf0\x22\xd1\x28\x48\x5a\xc3\x9d\x9d\x9e\x69\x3e\xf8\xff\xb1\xf7\xee\xfb\x6d\xdb\xcc\xa2\xe8\xdf\xc9\x53\xa0\xdd\xe7\x6b\xa4\x98\xb6\x75\xb7\xa3\xc4\x5d\xcb\x91\xed\xd8\x2b\x71\xec\x6d\x3b\x6d\xbf\xed\x9f\xeb\x1f\x25\x42\x16\x1b\x89\xd4\x22\x29\x5f\xd2\x78\xbf\xcf\x79\x8e\xf3\x62\xe7\x87\xc1\x85\xb8\x52\x94\x2f\x69\xda\x65\x7f\x6b\xa5\x22\x09\x0c\x06\xc0\x60\x30\x18\xcc\x05\x1e\xd9\x20\xfd\xd8\x6d\xb5\xe0\x99\x9b\x11\xfd\xd8\x6d\x51\xf0\x8c\xb3\xff\xd8\x6d\x51\xb4\xf8\x65\xef\x8f\xdd\x16\x69\x90\x1b\x01\xfd\xd8\x6d\x35\x6f\xcf\xbc\xe6\xab\x27\x7b\xc4\x27\x7b\xc4\x7f\xb6\x3d\xa2\xcb\x18\xf1\xde\x36\xf3\xe5\xcd\x04\x4b\xd8\x00\x42\xb9\x8f\x38\x7b\x4c\x13\x7b\x78\x3b\xdf\x64\x25\x37\xae\xbf\x8b\xcd\x4a\x09\x93\xfa\xd5\xd5\xd5\x3c\x26\x8d\x2d\xce\x0d\x4b\xd8\x48\x58\x3c\x80\xc3\xd9\x08\xf9\xd3\x50\xc2\xfd\x91\x0e\x24\x66\x32\x7a\x4d\xe0\x51\x33\xd6\xdf\x55\xb8\xc2\x38\xd1\x75\xe3\x46\x2b\xae\x42\x0b\x08\x7c\xb2\xf8\x65\x6c\x6a\x1f\x71\x66\xd9\xd4\xd4\xcd\x4b\xde\x5d\x6e\xcf\xbc\x56\xed\x69\xb7\x78\xda\x2d\xfe\xd9\xbb\xc5\x77\x6a\xbd\xfe\x70\x86\xe6\x25\xed\xe0\x73\x53\xce\x43\x9c\xa4\x71\xe4\x8f\x9f\xec\x39\x1f\xdb\x9e\xf3\xb6\x9c\x85\x5f\x84\xaf\x72\xb3\xc1\x22\xfd\x70\x5e\xd0\x54\x11\x4f\xd9\xac\x9e\x5b\x0b\xdd\xe3\x86\x32\x9c\x90\x8d\xe0\xc8\xbf\x7a\x8f\xe7\xdd\x5c\xc8\x45\x5f\x78\xcf\x9f\x3d\xd3\x71\x33\x0a\x14\x78\xa6\x95\xbf\x89\x33\xdb\x11\x1f\x24\xc3\xbd\x67\xcf\x4a\xde\x4d\x97\xbe\x82\xc3\x83\x23\x3c\x88\x2f\x69\x70\xa8\xa2\x3b\x2b\x5e\xce\x8a\xab\xfa\xb5\x60\x40\x66\xd1\x38\x1e\x7c\x2e\x47\x29\x4a\xd9\x02\x62\x71\x95\x2b\x63\xf0\x58\x6e\xdc\x9c\xa3\xf7\xc0\x37\xdf\xf9\xdc\xcf\xbd\xfe\x5e\xe4\x9a\xd3\x76\x6d\xec\xec\x52\xf9\xf9\x29\x37\x3b\xc5\x73\xb3\xc8\x5d\xa6\x3e\x37\x1a\xf2\x36\xc9\x9a\x35\x2c\x35\x22\x2d\xde\xfc\xad\x42\x41\xd2\xed\x09\xa7\x6a\xd7\x6d\x87\xf3\x52\x44\x02\x27\xcb\xbb\x8f\x77\x3e\xd8\x9c\xa3\x16\xce\xa7\x43\x2e\xec\x10\xcb\x4d\xb9\x9c\x6f\xb7\xb9\x70\x6e\x51\x11\x69\x5a\x21\x5d\x4e\xaf\x3f\xc9\xe9\x4f\x72\xfa\x3f\x5b\x4e\x67\x42\x7a\x3a\x72\x68\x75\xe6\x88\xdf\x38\xc1\xb3\x09\x01\xfd\xf3\x1c\x25\xd0\x20\x4e\xf0\x4a\x18\xab\x72\xfa\x5a\xe9\xc0\x09\x25\x1d\x2d\xe7\xf9\x6b\x42\xa1\xe3\xd1\xe8\xd1\xb5\x43\xdf\x8f\x3c\x4e\xb8\xe3\xf1\x48\xb9\xdd\xc0\x57\x2c\xe8\xf4\xce\xb7\xb8\xd0\x49\x47\xf3\x2f\x74\xd2\x11\x5c\xe8\x50\xc1\x65\x91\x7b\x9b\x22\x39\xdf\xbd\x39\x19\xe2\x81\xb4\x35\x5d\x5a\x6f\xea\x98\x88\x90\x8e\x46\xe7\xf6\x02\xaa\x51\x08\xb2\xe8\xb2\x8a\x1a\x0d\xa3\x61\xec\x6e\xd1\xf2\xf5\x7e\xcd\xa5\x38\xdb\xf7\xaf\x19\x11\x1c\x87\x5f\xf4\xcb\x61\xa9\xed\x79\x45\x55\xab\x9f\xbb\x20\x12\x46\x87\xf1\xaf\xc5\x08\xd8\x8a\xdc\xaf\xe1\x89\x9f\x7c\x3e\x49\x66\x69\x86\x83\x43\x6c\x5c\x06\x4b\xcd\x17\x17\xbc\x1f\x12\x11\x26\x32\xdd\xa1\x1f\x16\xb4\xef\x2c\x73\x3f\x0a\xf0\x83\xe0\x30\x09\x2f\xfd\x0c\xd3\x23\xa1\xa3\xf5\xa2\x62\xf7\xeb\x3b\x4d\xfa\x35\xb7\xfb\x45\xc5\xee\x87\xc0\xc8\x4f\xe7\xb6\xee\x2c\x73\xbf\xa6\x2f\x70\x46\x37\xf4\xc2\xb1\x2f\x28\x75\xff\xe6\x4b\xcc\x7d\x51\xb1\x7b\xd3\xfd\xf1\xcd\xa4\xb0\x71\x57\x91\x7b\x53\xfd\xbc\x86\x5d\x45\xee\x3b\xe4\x44\x8e\xcb\x30\x05\xbd\x93\xc4\x93\x43\x3f\x4d\xaf\xe2\x24\x28\x1a\xff\x92\x75\xee\xbd\x0e\xe6\x8d\x89\xab\xc8\xbd\xc9\x70\x5e\xc3\xae\x22\x0f\xc1\x7a\xe6\xb5\x5d\x50\xca\xde\xbc\x78\x58\x5d\x45\xe9\xac\x0f\x37\x6f\x34\xa5\xf1\x2c\xca\x9f\x27\x61\x9a\x86\xd1\xc5\xf3\xd2\xd8\x4e\xe3\x54\xbf\xba\x92\xb0\xb4\x7c\xb5\xe8\x29\x50\xb1\xde\x11\xcd\xbf\xe5\x3a\x1e\x8d\xa4\x04\x62\x9a\xed\x85\x72\x8a\xd6\x2c\x23\x5a\x8d\xa7\x33\xf4\xd3\x19\xfa\x9f\x7d\x86\xce\xef\xba\xfa\x5f\xbe\x68\x77\x5d\x9b\x63\x7c\x8d\xde\xe2\x04\x5f\xa4\x5f\xfc\xf4\x4b\x88\xde\xf8\x63\x7c\xfd\x9f\x49\x36\x4c\x57\x46\x33\xf5\x38\xdc\x61\xd1\x4c\x8f\xf0\x10\x27\x38\x1a\xe0\x2e\x22\xed\xa7\xdd\xd5\xd5\x8b\x30\x1b\xcd\xfa\x2b\x83\x78\xb2\xca\x4f\xdd\xab\x17\xf1\xb2\xf8\xdd\x1f\xc7\xfd\xd5\xf4\xca\x4f\x26\xab\x61\x94\xe1\x24\xf2\xc7\xab\xa4\x4b\xf8\x3a\xe3\xff\x5d\xb9\x88\xff\xd7\x87\x66\xf3\x91\xaf\xc6\xf2\xfb\xae\x63\x82\xcd\x3f\xfc\x70\x0d\x3f\xfe\x16\x97\x5d\xd4\xf2\x15\x67\x57\x71\xf2\xf9\x08\x43\xa8\xda\x22\x45\xb9\x5e\xdc\xd4\x96\xf7\xbf\x7c\x39\x2f\x28\x75\x1f\xdf\xbc\x9b\x68\xb0\x1d\xf9\xfd\x31\x9e\x87\xa5\x54\xd2\x8e\xa0\xbd\xc0\x7d\x70\xbb\xf2\xa7\x25\x71\xcb\x4b\x3a\x70\xb3\x16\xb8\x07\x6e\x41\x7c\x15\xb1\x28\xc4\x45\x88\xf1\x62\x76\xac\x2c\x5f\xcb\xbb\x9b\x3a\x10\x9b\x4d\x4b\xa0\x45\x0b\xd9\x91\x32\xbe\xdd\x1b\xa5\x04\x67\x49\x88\x2f\xe7\x45\x80\xe0\xc5\xec\x68\x59\xbe\xde\x87\xb4\x32\xb2\xdb\xcd\x21\x2a\x52\xc6\x41\x4e\xda\xa7\x7b\x0f\xd1\x05\x2e\xe1\xce\x6c\xc7\x45\xfd\x70\x8f\x31\xa1\xd9\x1b\xe6\xc4\x48\xb5\xe3\xa0\x7e\xb8\xf7\x68\xb0\x84\x2d\xc5\xc8\xd0\x42\x76\x7c\x8c\x6f\x1c\xa5\x56\x29\x94\x0a\x6e\x75\x0d\x15\xa7\xce\x96\xa5\xdb\xbf\x9c\x1f\x4a\x2f\x73\x46\x94\xbf\xe4\x7c\x40\xba\x71\x9c\xaa\xcf\x9c\xfa\x25\x40\x84\x04\xf3\xc7\x0b\x2c\x5d\x4c\x4e\x67\xd2\x83\x24\x8b\x3f\xea\x35\xe3\x28\xbc\x74\xfa\xc6\x90\x39\x81\xef\xce\x33\x64\x31\x6c\x8b\x52\x56\x81\x0d\xdf\x1d\xc7\x2b\xcb\xf9\x8a\x08\x4b\xb6\x60\xb7\xd6\x7b\xc9\xe6\xd3\x99\xea\xe9\x4c\xf5\xcf\x3e\x53\xb1\x03\x15\xbf\x20\xfa\xb6\x51\xda\xef\x62\x58\xcd\xbd\xa3\xfc\x69\xc8\x85\x71\x9a\xe2\x2f\x1b\x15\x59\xa0\xd1\xeb\xb2\xc2\xa8\x94\xbc\x74\x76\x33\x25\xf2\x01\x8b\x40\xf9\xfa\xb9\xc4\xc0\xc3\x6c\x30\xaa\x90\xef\x7a\x6e\x91\x81\x9f\x62\xf4\x82\x50\x7c\x9a\xbd\xe8\x2a\x9f\x60\xb2\x92\x8b\x74\x25\x1d\x85\xc3\xac\xa2\x25\x14\x41\x46\x72\xc0\x9a\x59\x80\xb1\x64\x70\x5f\x8b\xf0\x15\x8d\x40\x45\x2f\x64\x5f\x5b\xd0\x98\xe2\x28\x08\xa3\x8b\x47\xc7\xe3\x90\xb6\x23\xdb\x10\xd9\x90\x62\xa1\x03\x4d\x6c\x34\x70\x46\x65\x9a\x60\xe5\x56\x92\x0e\x44\xa9\xf9\x96\x84\x0c\x9a\x2e\x23\x28\xa4\x60\x91\x9d\x2c\x52\x75\x14\x46\x69\xe6\x8f\xc7\xa5\x5a\xd6\x4a\xdb\x7d\xdd\xdd\x85\x0a\xf0\xb8\xc0\xd9\x87\xf8\xa2\x44\x40\x31\x52\xca\xe9\x63\x4f\x5b\xd4\x8a\x14\xb4\x3a\x8d\xe7\xc6\xe1\x20\x45\xe6\xb4\xd7\x1b\xf9\xd1\x05\x2e\xd1\xa4\x4d\xf8\xa0\x20\x64\x93\x2c\x65\xf4\x14\x41\x88\x74\x4c\x6a\x24\x1e\x8f\x65\x79\x60\x61\x7e\x93\x8e\x46\x2b\xc0\x1a\x0d\x76\x93\x8e\x4c\x76\xe3\x16\x9f\xe6\xdc\xd2\x18\x64\x80\x8c\x5b\x1a\xc5\x92\xe0\x41\xd5\xf4\x6e\x62\x44\x36\x4d\xfd\xe3\x21\x62\x92\x2e\x32\xae\x29\x68\xb3\x0c\x07\xbd\xe8\xfd\x9a\xd7\xc8\xf8\x01\xda\x96\x49\xcf\x90\x44\x29\x0e\x38\x1b\x75\xc9\x3f\x14\x58\x3a\x1a\x75\xc9\x3f\x54\x78\xb5\x25\x64\x68\xb5\x9e\x44\xd2\x27\x91\xf4\x1f\x2e\x92\xe6\x7a\x7e\xee\x63\xfd\x40\xd9\xa2\xa9\x7f\xf8\x11\xbe\x20\xf3\xec\x27\x9b\xfd\xd0\x91\x97\x20\x5d\x7d\xa7\x16\x85\x44\xff\x5c\x3d\x1f\x0e\xfc\xa9\x0c\xc4\x05\x63\xaf\xb7\x79\x68\x42\x90\x30\x61\x8e\xe8\xcc\x7a\x19\x6d\xa0\x17\xb5\xeb\x41\x27\x78\x15\x34\x06\x41\xab\xf5\xca\x5f\x6b\xb7\x06\xad\x57\xad\x46\xa7\x85\xeb\xeb\xb5\x57\x83\x76\x0d\x37\x5b\x41\xa7\xd5\xee\x34\xfa\x2f\x72\x5c\x6c\x60\xfc\xba\x5f\xaf\xd7\xfb\x83\xda\x5a\x6b\xf0\x6a\x30\xf4\xd7\xd6\xeb\xc3\xda\xa0\xb9\x8e\x3b\xcd\x7e\xd0\xae\x0f\x5e\xd5\xfb\xeb\xfe\xb0\x56\x7b\xe1\xe6\x4d\x14\xc7\xae\x24\xe9\xfa\xfd\xb0\x6b\x19\xc4\x9c\x13\x32\x37\xf8\xae\xb5\x7f\x74\xa7\xa7\x85\x09\xda\x06\x64\x7d\x5c\x2d\x70\xcd\xee\x52\xa8\x0a\xc7\x2c\x9e\xc5\x1f\xbb\x75\xef\xc7\x39\xf3\xf4\x63\xb7\x41\x98\x6d\xfb\x89\xd9\x3e\x31\xdb\x7f\x36\xb3\xcd\x79\x2d\x57\x7e\x69\xcc\xb6\xc8\x30\x79\x98\xc4\x5f\xf0\xc4\x8f\x56\x02\xfc\xf3\xb7\x4a\xe7\xaf\x5f\x90\xde\x27\x5d\x3f\x55\x24\x4a\xdf\xe9\x0b\x25\x23\x81\x56\x22\xd5\x4b\xdc\x25\xf7\xfe\xe2\x19\xfe\x0b\xb2\xf5\xf3\xb1\x78\xfc\x7c\xfd\x65\x73\x7c\xdf\x3b\xc5\xb7\xa5\x4b\x05\x49\xbe\x6d\xa1\x5b\xb4\x11\xfe\x0f\xdb\x5b\x5a\x17\xd2\x6d\x7f\x27\xe9\xb5\x9d\xfd\x7e\xa0\x04\xdb\x3f\x6c\x50\xc2\xd1\x5e\x91\x0d\x65\x18\x46\x38\xb8\x4f\x06\x6e\x9e\x39\x36\x8b\x11\x4b\x5b\x9d\xa7\xb3\x86\x74\xdc\xe2\xb0\x2d\x12\xb2\xae\xa0\x7d\xb2\xb1\x85\x38\x65\x94\x04\xc3\xa4\x8d\xa5\x96\x03\x7b\x6e\x36\x7d\x3e\xae\x3b\x6c\xa4\xbe\x7e\x9c\x8d\xc7\xb7\x92\xb1\x7b\x38\x44\xf8\x3a\x4c\xa1\xb8\x75\xc8\xb5\x16\x0b\x73\xca\xf3\x0c\x36\xbc\x35\x9a\xc3\x46\xce\xbb\xbf\x8c\xea\x67\x55\x47\x9a\xf9\x95\x69\x3c\xad\x54\x21\x6f\x35\xbb\xf7\x22\xfc\x1f\xd6\x13\x8c\xd6\x0f\xb2\x70\xa3\x0e\x37\xb5\x6f\xc8\x31\xcb\x62\x3b\x29\xaa\x76\x10\x2e\x62\x64\xaf\x78\x2f\x9c\xd4\x58\x3e\x7d\x37\xd4\x11\xa2\x24\xe2\x09\x4a\xf2\x74\xde\xef\x70\x56\x91\x4e\xe7\x38\x9a\x4d\x70\xe2\xf7\xc7\xb8\x8b\xb2\x64\x86\x4d\xd5\x9f\x3f\xc1\x69\x61\xaa\x6e\x29\x9f\x37\x14\x06\xe5\x2d\x92\x72\x78\xa7\x73\x92\x78\xa7\x5a\x16\xef\xd4\x91\xc6\x5b\x2f\xf2\x5a\xd1\x88\x89\xe6\xeb\x3c\x7b\x3f\xed\x84\x3d\xbb\x4b\xdc\xff\xc3\x83\xf2\x1e\x1d\x32\xd6\x17\x02\xdf\x4f\x6f\xa2\xc1\x3b\xd8\x6f\x88\xc8\x0b\x5d\xa8\x9e\x29\x39\xd1\x37\x59\x91\x8a\xe4\xa6\xa1\x55\x53\x26\x09\x40\xa8\x2c\x03\x6e\x97\xd1\x12\xe0\xb0\x32\x18\xf9\xc9\x66\x56\xa9\x55\x57\xb2\xf8\xd3\x74\x8a\x93\x9e\x9f\xe2\x4a\x95\x7f\x86\xac\xca\x95\x7a\xd5\xb9\xf1\xf0\x99\x75\x67\x1e\xcd\x37\xee\x3c\x8d\x2a\x8f\x88\xc6\x6b\x5c\x90\x0e\x99\x2b\x46\x08\x28\x4a\x82\x6d\xf1\xd6\x96\x62\x5b\x55\xf4\xf0\xcc\xf6\xa2\x0a\xdd\xee\x25\x8d\x4d\x9e\x8a\xbb\xa8\x83\x7c\xd4\x17\xeb\x65\x7e\xd7\xef\x0e\x02\x86\x72\x33\x27\x6b\x87\x68\xda\xf3\x05\x7b\x55\x32\x3d\xba\x9a\x12\xdd\x3e\xd8\x66\x52\xf4\x5b\x35\x7b\xf9\x05\xce\x16\x4c\x5e\x7e\x81\x5d\xdb\xc9\xf7\x9d\xbb\xdc\x42\x1c\xe5\xb3\x97\xeb\x66\x73\x5d\x59\x1e\x35\x95\xe4\xa7\x67\xaa\x7a\x9d\x4c\x13\xab\xa2\x6d\x56\x25\x13\xa1\xcb\x53\xf6\x58\xe9\xd0\xf9\x00\x49\x07\x73\x2d\x66\x0f\x39\x62\x77\x9e\x8e\xd8\x4f\x47\xec\x7f\xf6\x11\x5b\xd2\x67\x32\x0e\x31\x61\x2c\x5d\x3d\x69\xff\x17\x1e\x0e\x13\x7c\x83\x7e\x0d\xc7\x83\xcf\x18\xbd\xf9\x03\x0f\x87\xae\x68\x3d\x0b\x85\xf6\xd9\xf7\x13\x72\x84\x3f\xf0\xa3\x01\xf6\xa1\xac\x2d\xa8\xcf\x1d\xe2\x00\xb1\x2a\xef\xfc\x4b\xf4\x6b\x1c\x07\xe8\xcd\x85\xf3\x90\xdf\xca\x0f\xf9\xff\xc5\xb8\xa9\xe2\x3c\xcc\x58\x6c\x51\x4a\x5b\x4b\xa0\x3a\x3d\x0b\xad\x2d\x05\x2d\x4e\x92\x58\x0b\x1e\xb4\x4a\xdf\x51\x1b\x04\xba\xed\xec\x65\x2f\x52\xb2\x31\x4e\xe3\x28\x0d\xfb\x63\x4a\x60\x53\x1f\x9c\x48\xd0\x84\xdd\xf9\x90\xbd\x68\x9a\xc4\x97\x61\x80\x93\x54\xd4\xf2\xc7\x69\x6c\x56\x8d\xc7\x63\x52\x95\x50\x1b\xb7\x1e\x47\x51\x1c\xd0\xaf\x61\x34\x88\x27\x32\x64\x02\x8c\xe5\x14\xa0\x57\xae\x59\x38\xc1\x64\xb1\x85\x29\xaa\xa3\x14\x0f\xe2\x28\x80\xdd\x31\x8c\x2e\xc6\x38\x8b\x23\x18\x4e\xd2\xbd\x82\x83\x3e\x47\x55\x39\xee\xf3\x97\x68\x43\x74\x45\xd2\x33\x90\xb6\x41\x03\x7c\x2b\xbd\xe4\xb8\xc8\x5a\x07\xe7\xe1\x8f\x48\x28\xa3\x24\x8e\xe2\x59\x3a\xbe\x81\x30\x18\x8e\x7d\x98\x7c\xb2\x9c\x47\x50\xe0\x67\xbe\xf3\x84\xac\xf6\x56\x51\x79\x44\x81\xd2\x79\x02\x46\x3e\xa9\xfd\xa0\xf4\x5e\x49\x6e\x18\x47\x69\x4c\xb6\x2e\x42\x14\x15\x4a\x1a\x2b\x7b\xd1\xa5\x3f\x0e\x83\x43\x56\xbe\x22\xcb\x3c\xdc\x0b\x1b\x06\x43\x92\xf0\xd5\x3d\x9e\x91\xf9\x4a\x16\x1f\xd2\x77\x80\xd2\x0a\xed\xbd\x07\xdd\x64\xc6\x16\xd2\xf9\x85\x9d\xca\x37\xd4\xb9\xa2\xc2\x2c\x03\xcd\xaf\xca\xa1\x53\xbc\x91\x30\xfd\x85\xa0\x7b\x44\xa9\x10\x0b\x41\x4d\xea\x66\x36\x4a\xe2\x2b\xa4\x76\x4f\x2f\xaf\x74\x87\x75\x93\x7e\x5a\x29\x75\xf2\xf7\x17\x9a\x7d\x90\x66\x0b\x49\x40\x3f\x97\x0a\xe9\x67\x3e\x31\x00\x70\x83\x22\xa4\xe0\xb9\xa5\x68\x83\xa7\xce\x94\x64\xe3\x22\xea\x78\x18\x42\x30\xe7\x9e\xca\xfd\x0c\x64\x09\x79\x9e\x74\x0a\x27\x89\x2e\xe2\x5b\x7a\x53\xd5\xcd\x6d\xc8\x9f\x02\x67\x11\x1a\x9b\x3f\x64\x46\x6d\xb9\x7d\x43\xc8\x65\xd9\x5e\x15\x12\xd4\x83\x73\xba\x8f\x0d\x36\x6a\x2c\x3a\x19\x90\x02\x6f\xc9\x77\x8b\x92\x89\xd6\x7b\x08\xc2\x84\x16\xbe\x33\xc2\x04\x9c\x64\xea\xe4\x4c\xe6\x6e\xa4\x98\x3e\x00\x2d\xaa\x34\xc8\xf5\x6c\x30\x1b\x15\xde\xca\xbd\x48\x2f\x9d\x47\x7b\x4a\x87\x04\xd1\xa1\x39\xdb\x1f\xce\xc4\xbe\x4a\xa4\x4d\x7e\x26\x64\x22\x9f\x41\x71\x19\x9f\x2a\xbb\x6a\xae\x90\x96\x44\x5d\x75\xd7\x77\x6e\xf7\xf3\x76\xee\x8c\x1c\xa9\x98\xe0\xa2\x23\x4a\xbe\x1d\x8a\x4f\x73\x39\x36\x8d\xfd\x7f\x0b\xd0\xf6\x82\xb9\x4b\xc6\xf2\x55\x98\x25\x71\x4c\xb2\x38\x88\xd1\x60\x8c\xfd\x68\x36\x45\x11\xc0\x27\x03\x2c\x8e\xed\x45\x43\x25\x61\x6f\x59\x79\x14\x49\x39\x20\x8a\x68\x5c\x1d\x4b\x22\x1c\x9d\xd2\xd2\x67\x44\x48\x22\xd5\xbb\x88\x02\x09\x83\xae\x01\xa8\x6b\x03\xd9\xcd\x7f\xde\xf2\x9c\xd8\xab\xab\xfa\xe8\x2b\x0c\x80\x09\x60\xea\x6e\xce\x10\xaa\x88\x15\x3e\x67\x72\xe3\xa9\x10\x4a\x89\x08\xca\xcc\x68\xe1\x74\x73\x11\x92\x23\x5d\xa8\xeb\x8e\x49\x1d\xcb\x9c\x1b\x73\x5b\x38\xf2\x02\x84\x4a\xa4\x50\x97\x77\x88\x5a\x96\x59\x06\xf9\xb5\x34\x3c\x39\xfe\x6c\x74\x2a\x4c\xa3\xfa\x19\xdf\xa4\x95\xbc\x6e\x95\x6b\x79\x21\x59\x3c\xfa\xe9\x27\xe4\x1a\x43\x42\x4c\xc9\x09\x7d\x5f\x51\x0a\xbd\x56\xc7\x59\x17\x80\x0b\xc6\x3b\xdf\x7d\x12\x4c\x78\x01\x91\xff\xf9\xb0\x4f\xf0\x60\xe4\x47\x61\x3a\xe1\xc7\xd0\x62\xe6\x00\x00\x8a\x87\x97\xb6\x21\x0f\xec\x67\x8c\xa7\x22\x7f\x00\xef\xec\xea\xcb\x3f\xd2\x51\x18\x91\x86\xae\x07\xf1\x64\x3a\xc6\xd7\x61\x76\xd3\x6d\xc3\x91\x8c\x14\x20\x04\x51\x21\x9b\xc3\x67\x7c\x43\x35\x05\x62\x34\xa5\xf1\x5a\x5d\x45\x09\x9e\xc4\x97\x18\xf9\xe3\x31\xf4\x2a\xf5\x10\xbe\x1e\xe0\x69\x06\x62\x3f\x7b\x25\x97\xcf\x46\xf8\x06\x45\x98\x8e\x48\x1f\xb3\xfa\x01\xe9\xf1\xcc\x1f\x8f\x6f\x50\xff\x06\x86\x8c\x0c\x0f\x4b\x05\x00\x34\xf3\x2b\xd9\x90\xc2\xe8\xa2\x52\x95\xf6\x81\xca\x0f\x4a\xef\xd0\xd7\xaf\x04\xdf\x95\x30\x0a\xf0\xf5\xc1\xb0\x02\x6e\x8a\x84\xd8\xce\x5f\x54\x61\xf2\x97\xeb\xfa\x06\x21\x51\xd8\x67\x7c\x73\xb6\x22\x56\xa2\x6e\x0e\x6d\x52\x24\x29\x6f\x98\x26\xff\x8d\xc9\x13\x4e\x99\x64\xde\x07\xd4\x36\x17\xc5\x51\x19\x9e\x40\x4d\x6a\x8b\x68\x92\x59\x0c\x9b\x2a\x50\x07\x15\xa2\x0e\x01\x67\xe9\x4c\x8a\x33\xa5\xf7\x04\xb0\xa4\x8a\xf4\xd0\x60\x65\xfb\x64\xf7\xfc\xf0\xe0\xc3\x87\xbd\x8f\xef\xce\x4f\xf6\xf6\xb7\x0f\x3e\x9d\xc8\xc7\xa3\x32\x33\x60\x0a\x55\x8a\xc4\xf4\x28\x47\x47\x53\x26\x23\x78\x6d\xf9\x99\x8f\x36\xd0\xe9\xd9\x6b\xf5\xfd\x1e\xb8\x1b\xf3\xd7\xe5\x96\xaa\x00\xb8\x32\x9d\xa5\xa3\x8a\x4e\xf7\x4c\xc4\x53\x4a\xef\x05\x29\x2d\xfc\x19\xdf\x54\x8d\x31\xc8\x01\x2e\x30\x78\xa5\xc4\x4d\x01\x99\x35\xca\x97\xd4\xc4\x9f\x2a\x4c\x32\x04\xb2\x05\x86\x02\x24\x46\x48\x53\x1d\xa6\x7d\x7f\x2a\xa9\x2e\x24\xbd\xb6\xea\x29\x4e\x05\x57\xe0\x1a\xd5\x3f\xf5\x31\xd8\xf7\xa7\xa7\x50\x2d\x84\x2d\x9e\x8f\xcc\x29\x14\x3f\x93\x3c\xd2\x45\xe3\x8a\xdf\x3c\x5a\x58\x66\x8e\x55\xa9\x59\x09\x6f\x72\x72\xb0\x75\xd0\xe5\x44\x86\xc6\xf1\xc5\x7f\xe8\x52\x75\xec\x90\xab\xef\x2b\x49\x97\x50\x16\xa4\xd6\xa3\x23\xfb\xb6\x32\xf1\xa7\x15\x97\xb1\x02\xff\x03\xfb\xc5\x20\x1f\x65\x32\xf6\xec\xa8\x17\x06\xb2\xe3\x8d\xa0\x88\xcf\x18\xa5\xb3\x04\xf4\xc4\x9c\x59\x85\x29\x4a\xb3\x90\xd0\x03\xe5\xe4\x38\x40\xfe\x10\x1c\x84\x92\x24\xbc\xf4\xc7\xda\x5e\xab\xc0\x24\x03\x02\x6e\xff\x74\x69\x84\xc1\x99\x8e\x62\xde\xa5\x95\x41\x6e\x0f\xa0\xd6\x11\x5f\x9c\x0e\x33\x5c\x77\x22\x7f\xba\x45\x78\xcc\xf4\xcc\x96\x1a\x43\x7f\x9c\x62\xf9\x96\x8d\xb9\x3d\xcd\x1d\x53\x91\xce\x9f\xb5\x89\xee\x00\x83\xcc\x0b\xcc\xb8\xb4\x68\x1d\x87\xff\xd7\xc6\x78\xfe\x00\x35\x4b\x8c\x63\x79\xc5\x00\x52\x28\x4c\xea\x25\x54\x54\x47\x49\x5b\xec\xee\x61\x52\x71\x71\xeb\x19\x90\x7c\xc9\xe9\xca\xb8\x74\xa4\x07\xd5\x50\x6f\xbc\xb4\xd4\x4b\x66\xea\x0a\xa6\x90\xfe\xb1\xdb\x80\xd0\x3e\x4c\x19\xfe\x63\xb7\x09\x6e\xa8\x6b\x65\xee\xc8\x58\xcc\x4d\x9c\x65\x61\x74\x61\xf7\xec\x05\xc6\x14\x48\x99\x6b\xd1\x86\xf0\x59\x7b\x6d\x94\xc8\x23\x3d\x0b\xfb\x20\x57\xd0\x22\xd6\x28\xeb\x37\x41\x79\xfd\xe9\x5a\xef\xe9\x5a\xef\x1f\x7e\xad\xc7\x22\xfa\xb2\x53\xcb\x5d\xa2\xfa\xce\x33\x87\x75\xe4\xbe\xd0\x52\x5f\x2c\x62\x38\xcb\x97\x74\x9d\x1d\x0e\x36\x83\x20\x85\xa1\x13\xbb\x9b\x1f\x81\x5a\x2a\x45\x33\x2a\x7e\x31\xa7\x37\x8f\x08\x5f\x61\x06\x91\xf2\x10\x24\x05\xa0\x9b\x2a\xdd\xed\x9f\x3f\x97\xcf\x07\xec\x7c\xf6\x5c\x57\x12\x91\x6d\xf3\x39\xbb\xb6\x92\xca\x49\xbc\x8a\xc6\xe9\xe1\xae\x74\xa4\x5c\x1c\x31\x87\x2b\x85\xa3\x31\xb9\x89\x8c\xbd\x45\xd5\xe8\x12\x8a\xe8\xbe\xcd\x7b\x9a\x5a\x36\x0b\x9b\x3d\x0e\xff\x53\xf7\x2d\x7d\x7b\x72\xe9\x2e\x85\x85\x20\x0f\x44\x04\x28\xff\xf4\x13\xe0\x4e\x15\x53\x61\x74\x01\xdc\xb8\xaa\x40\xe4\xd7\x17\xf3\x52\x9a\x52\x88\xb2\x97\xf2\x5d\x3b\x29\xa4\xa1\xb1\x9f\x42\x33\xc7\x19\x99\xec\x1f\x36\x36\x8c\x81\xe6\x7f\xc6\x8b\xd5\x55\x9a\xb9\x5d\x21\x29\x58\x6a\x59\x32\x23\x32\x5b\x92\x66\x28\x8d\xa9\x9d\xe3\x74\x0a\xac\x1b\xce\xce\x7e\x74\x93\x91\x03\xbf\x87\xfa\x78\x48\x18\x00\x5d\xe2\xfc\x0a\x15\x46\x83\x2a\x19\xb5\xbf\x70\x58\xf9\xc1\x82\xf5\x4f\x3f\x21\xdb\xc8\x57\x8d\xfa\xc8\xbc\x6e\x20\xa8\x5a\xdc\xa3\x9d\x9d\x8d\x29\xdf\x8c\xf0\x75\x86\x7a\x87\x9f\xd0\xe0\x66\x30\xc6\x9e\xe8\x26\x0c\xbb\xd8\x6c\xa0\x27\xd0\x65\x66\xb3\x34\x4d\xe2\x01\xe1\x59\x29\x1d\x1d\xa3\x15\xe9\x18\x2c\x96\x89\x6d\x2e\x2c\x1d\x61\xa4\xa1\x97\xba\xf5\x50\xad\x4c\xff\x2c\xc3\x4a\x49\xc1\x25\x9a\x49\xc6\x60\xcf\x05\x00\xdd\x8c\x4d\xd2\xc5\x56\x4c\x3b\x28\x47\xba\x5f\xdd\x12\xea\xd6\xcb\x85\xf0\xbd\xc0\xcb\xd9\x04\x7b\x2f\xeb\x90\xa8\xce\x00\x38\x0b\x59\x27\xdc\x4e\x72\xcf\x9a\x96\xd3\x99\x6b\xb3\xd8\x64\x5e\x93\xff\x90\xac\x6b\xda\x23\x72\xb4\xa4\x9c\x5a\xa2\x5c\x78\x69\x49\x2a\x27\xd6\xab\x74\xd2\x87\x0f\x7e\x10\x08\xdb\x2e\x29\xef\xa7\xf8\xae\x4f\x8f\x74\x70\x90\x58\x2c\x37\xde\x82\xf7\x92\xad\x38\x15\xe8\xc4\x48\xc8\x96\xbe\x79\xbb\x85\x16\x8b\xe1\x30\x7f\xa5\x6a\xa5\x72\x16\x04\x5a\x05\x0d\xf9\x52\x48\xc8\xb3\xe8\x96\x68\x0d\x02\x13\x2a\xe7\x8a\x34\x07\xd5\x82\xd1\xb6\x4a\xb5\x02\x21\xb7\x01\x1b\x91\xd5\xd5\x6c\x17\x44\xf6\x7d\xca\x51\xfa\x24\xfb\xfe\xd3\x65\xdf\xdc\xa4\x8d\x27\xec\x7d\x28\x1f\xdd\xbd\xbe\x1f\xa9\xd2\x6e\xd8\xf7\x85\xeb\x2d\xbe\xa6\xea\xea\x22\xd7\xdd\xe3\x89\x9f\x64\xdb\xac\x60\xee\x76\xeb\xbc\x1a\x03\xb5\x12\x34\xcb\xfb\xa2\xe9\xbc\xa5\xd7\xe2\x12\xec\x38\x4b\xc2\xe8\xe2\x16\x5c\x5b\x6c\xef\x89\xb4\xdc\xf7\x23\xf9\xd3\x2f\xfe\x78\x86\x6f\xd1\x25\xf9\x0f\xbb\x0e\x21\x90\x87\x38\xc1\x73\x6e\x48\x3d\xd5\xbc\x00\x82\xd4\x30\x9c\x54\xb1\x38\x1b\x79\x80\x11\x91\xd6\x3d\xda\x92\xb9\x85\x81\xda\x8d\x8e\x32\x64\x9b\xee\xfb\x51\x25\x8b\xab\x4c\x55\x04\x3a\x1c\xf2\x99\xab\x7c\x2a\x16\x2b\x22\x52\x0f\xd2\x44\x54\x5e\x84\x54\x7d\x43\x21\x32\x3f\xdd\x17\xa6\xfe\x98\x41\xdc\x0a\x13\x22\x8b\xd9\x1c\x62\x78\x8f\x4e\x62\xe6\xd9\x2b\x77\x07\xaa\x33\xe8\x95\xaa\xd9\x35\xde\x9e\x90\x63\xa0\x1b\x36\x49\x17\x5c\x24\x84\xa7\x34\xce\x46\x72\x4a\xf0\x4a\x15\x1a\x61\xd8\x46\x69\x16\x66\x33\x2a\x70\x99\xe6\x5f\x01\x9e\xc6\x69\x98\xc9\x58\x32\xb8\x02\x3d\x00\x33\x18\x87\x38\xca\x74\x4b\x8c\xd2\x0d\x1b\x26\x16\x3c\xd5\xb8\x39\x82\x8b\x62\x64\x8e\x1f\x57\xc1\x17\x5e\x25\x0b\xd2\x1b\xce\xa2\x00\x6c\x22\x07\x38\xc9\xfc\x50\x4c\xbf\x63\xf9\x88\x89\x5d\x6c\x1d\x3d\xfa\x12\x12\x78\xdd\x61\x2d\xb1\x91\x27\xb3\xa9\x65\xfc\x92\x64\x5b\xe1\xbd\x9e\xc5\xb9\x44\x4b\x40\x77\x69\x03\x12\x6d\x8e\x67\xb8\x4b\xff\xc3\xc5\x5c\x2d\xd9\xbb\x73\x56\xd8\xe4\xe7\x93\x02\x71\xed\xc3\x01\xe2\x9c\x10\x71\x0e\x89\x2a\x93\x59\x9a\xc1\x56\x87\x27\x38\xca\x04\xdd\xf4\x6f\x32\x9c\x36\x1b\x55\x26\x8c\xff\x50\xd5\x26\x92\x95\x7b\xf0\xe9\x23\x93\xa0\x4d\x20\xaf\x4f\x49\x15\xcd\xa2\xf0\xbf\x67\x18\x85\x01\x8e\xb2\x70\x18\xaa\xac\xb8\xd4\x64\xf3\xe1\x29\x31\xc5\xd0\xa4\x9d\x6d\xfa\xb0\xed\x48\x9b\xd0\x6b\x9d\x0a\xf8\x20\x57\xfc\x7e\x58\x5d\xf1\x33\xc2\x59\x57\xf8\x00\x73\xd0\x7f\xde\x97\x0a\x0c\x61\x95\x0f\xa3\x35\x0a\x82\xb9\x21\xfe\xd8\x6d\x12\xd9\x95\x67\xee\xbf\x3d\xf3\xda\xa5\x72\x25\x33\xf5\x6e\xbb\x54\xc2\xb6\xd7\xb2\x16\x3e\x26\x02\xc6\xd0\x1f\x64\x71\x72\xe3\x51\x8d\x32\x19\xd8\x67\x84\x4f\x13\x59\x3f\x1e\x22\xd1\x9b\x8d\x0d\xf4\x23\x8d\xc8\xf4\x23\x94\x79\xb6\xba\x8a\x7a\xf1\x64\x12\x47\xff\x75\xfc\xfc\xd9\x33\xa3\xf3\xf9\x2f\xd6\x00\xc7\xa9\xf2\x23\x19\x86\x04\xff\x58\xf5\x90\xf4\x0a\x47\x83\xe5\xbe\x9f\xe2\x4e\x4b\xfb\x30\x09\xda\x7a\xd1\xcb\xe9\xe7\x60\xa8\xbd\x1c\x84\xd3\x11\x4e\x96\x29\xe4\xea\xeb\xe7\xcf\x6e\x9f\x3f\xc3\xe3\x14\x23\xa9\x33\x54\x63\x4e\xfb\xc2\x87\xe1\x47\xf4\xd3\x4f\xec\xc3\x8a\x3f\x09\x44\xdf\x36\xf7\xb7\x9e\x3f\x7b\x46\x3f\x54\x4e\x39\xce\x1e\x52\x51\x85\x67\x82\x21\xfd\x40\x11\x83\xdf\x32\x3e\x67\x62\x94\x65\xc4\x58\x43\x34\x1c\x06\xaa\xf4\x93\xf8\x2a\xc5\x49\xf5\xf9\xb3\x67\x62\xc4\xe2\x38\x5b\xe9\x25\x37\xd3\x2c\xfe\xaf\x63\x5a\xf5\x16\x8e\x4f\xf2\xfe\x23\xbe\xa3\x3f\x9f\x3f\x7f\x56\x51\xcf\x63\xcf\x10\x55\x89\x1c\x8f\xe2\x24\x1b\xcc\xb2\x94\xbe\x21\xcb\xa6\x87\x36\x10\xaf\xfb\x5a\x7a\x7d\x3e\x0e\xfb\xe4\xd3\xca\x38\xec\x4b\xef\x41\x1b\xd6\x83\x4e\x91\xaf\xa4\xd4\x8a\xf4\x4e\x81\xe0\x8f\x2f\x62\x00\x41\x7e\xbc\x7e\x2e\xb0\xf8\x10\xc7\x9f\x67\x53\x94\xf9\xfd\x31\x96\x30\x39\x7e\x7b\xf0\x1b\x3b\xf4\x89\x77\x7b\x1f\x7f\x39\xb7\xbd\x3f\xfe\xf4\xf6\x7c\x7f\xef\xb7\xf3\x9a\xeb\x43\xdd\xf5\xa1\xe1\xfa\xd0\xb4\xb6\xed\x6a\x47\xfe\x68\xb4\x25\x7f\x34\xda\x93\x3f\xf2\x36\xc5\xd0\xf4\xe2\xc9\x94\x9c\x14\xc7\xe6\x10\xd9\xa6\x54\xab\x15\xc4\xb3\x3e\x11\xfb\x49\xad\xbc\x00\xb0\x58\x19\x0b\x24\x9b\x2a\x84\x10\x4e\x10\x85\xe8\x0d\x6a\xb4\x3b\xaf\x51\xb8\xb4\xa4\x80\x17\x42\x22\x7a\x83\xea\x8d\x75\xe3\x1b\xf9\x0b\x4e\xc3\x33\xb4\x41\x60\xbc\x41\xf5\xd7\xea\x77\x7a\x97\x5a\x50\xab\x42\xab\x55\xd1\xef\xa8\x76\x5d\xaf\xf7\xf5\xfa\xf9\xe3\xed\x73\xa5\xd7\xbf\xfa\xe3\xcf\xe8\xdd\x4e\xa5\xf1\xfb\x7a\x55\xed\xed\x35\x0d\x91\xa8\xbe\x0b\xb5\x97\x0b\x8d\x80\x34\xc8\x69\x3f\xbe\x56\x3f\x82\xa5\x01\x69\xf3\x3a\x44\xbf\xa3\xca\x75\xde\x21\xf6\xbb\x21\xfd\x6e\x4a\xbf\x5b\x55\xad\xb3\x00\xa5\x92\x5e\xa3\x9f\x7f\xfe\x19\xad\x43\xc9\xf4\x1a\xfd\x84\x6a\xd7\xc3\x21\x1d\xa0\x4e\x53\xab\x42\x56\xc7\xe9\x35\x19\xc8\xf4\x5a\xfb\xc4\x17\xcf\x69\x0a\xdf\xaf\x5f\x3f\x77\x76\x6a\x32\x1b\x67\xe1\x74\x1c\x0e\x40\x4d\x60\x76\xef\x9a\x90\x71\x70\x7a\x7d\xf6\xda\xf2\xad\x45\xbf\x35\xac\x1f\xd7\xe9\xc7\xd6\x59\x41\xeb\xe9\xac\x8f\x40\xc0\xf1\xd0\x24\xbc\x46\x83\x78\x3c\x9b\x44\xa9\x42\xfd\x32\x4c\x22\x29\x54\x02\xe8\xd5\x4b\x42\x33\xb5\x3a\x1f\x29\xf6\x58\xab\xd7\x6a\xfa\xd0\x8a\x95\x4c\x07\xab\x92\xc1\xc4\xb4\xaa\xe8\x2b\xf9\x4d\xc7\xdb\x51\xa5\x2e\x57\xa9\x77\xa4\x2a\xf5\x8e\xab\x4e\x43\xae\xb3\x5e\x45\x79\x9d\x86\x31\xeb\x82\x1b\xd0\x3a\x59\xc1\x48\x85\xd1\xa5\x3c\x5a\xe4\xb1\xf4\x88\x5d\xaf\x4b\xe3\xc3\xc8\xb3\xc5\x5e\xd5\xf8\x8b\x86\x32\xa4\x85\x23\xaa\xf0\x47\x46\x63\x65\x86\x55\x61\x9d\x4a\xbd\x39\x63\xab\xb0\x55\xa5\xe2\x9c\x01\x56\x58\x2e\xab\x58\x34\xca\x70\x5b\x00\x8a\x60\x9c\x98\x9c\xf0\x87\x6b\x2b\x13\x64\x0c\x60\x63\x01\x0e\x08\x55\x1a\xe8\x77\x14\x9c\x92\xff\x5d\xaf\xa3\xdf\xd1\x75\xe3\xec\x4c\x5f\x48\x50\x36\x44\xbf\x6f\x40\xc1\xeb\xd0\x28\xa0\x30\x49\xf8\x79\x0b\x87\x5a\xb1\xaf\x1c\x26\x78\x40\x3b\x17\xa0\xa3\x41\x1c\xb1\x0d\x26\xdf\x95\x8e\x7a\x07\x1f\xc9\x1e\x51\xbb\xae\xd5\x3c\x54\xbb\xae\xd5\xe1\xdf\x06\xfc\xdb\x82\x7f\xd7\x3d\xa0\x05\xf2\x6f\x03\xfe\x6d\xc1\xbf\xeb\xf0\x6f\xbd\x4f\xfe\x6d\x76\xf2\xcd\xec\xe5\x4b\x86\xd4\x4b\xb4\xb9\x7d\x4c\x03\xb2\x23\x2a\x0e\x21\x22\x10\x24\x61\x36\x9a\xac\xf0\x32\xab\x39\x2a\xa4\xf4\x06\x13\x1f\x56\xe8\x83\x24\x61\xac\xe0\xeb\x8c\x86\x0f\x10\x5d\x3e\x0f\xe2\x23\x9c\xe2\xac\x8b\x1c\x5b\x24\x1b\x84\xe3\xcf\xe1\x94\x99\xfe\xc6\x43\x14\x1d\xc5\x70\x1c\x1b\xf9\x29\xea\x63\x1c\x81\x7b\x00\xbb\xe0\xf2\xa3\x00\x6c\xf8\x82\x30\x40\x51\x9c\x31\x3b\x4c\x93\x14\x68\x36\x17\x0e\x89\xdb\x8b\x9e\x7f\xc6\x37\x87\x49\x18\x27\x47\xd4\x04\x78\x63\x23\x7f\x6f\x25\x1d\x6e\x17\xa6\xcd\xa9\xd9\x01\x55\x7c\xe3\x7f\xdc\xe2\x70\xc3\xde\x7c\xfe\xd6\xc2\x9f\x3f\xe3\x9b\x5f\xe3\x04\xac\x18\x3f\xe3\x9b\x95\x2b\xf2\xdb\x5e\xec\x38\xfc\x82\x59\xa9\x34\xbc\x78\x4b\x18\x10\x5a\x45\xad\xa2\x65\x24\x1c\x01\x12\x18\x20\x13\x2c\x1f\x39\x8e\x63\xfe\xcc\x1b\x5c\x42\x9d\x52\x2d\x90\xfe\xa7\x83\x11\x26\xc7\x0f\x44\x44\x68\x4b\x1f\xd2\xa3\xf8\x8a\xc0\xae\xf0\x66\x96\xc8\x2e\xfd\xb2\xb0\x0f\x32\x5c\xfb\xb0\xf0\x46\xa5\x71\x96\xde\x9d\xea\x4b\x35\xb7\x11\x25\xe8\x50\xd1\x83\xfe\x7c\xc3\x30\x64\xcf\x16\x29\x04\x31\xb2\x13\xe5\xe9\x20\x59\xcb\x91\x3f\x09\x95\x53\xa8\x73\x46\x47\x16\x66\x9c\xbd\xb1\xb0\x1a\x37\xc3\x42\xd2\x7e\x62\x00\x87\x70\x3a\xfa\x50\xca\x68\xff\xc0\x10\xff\x97\x40\xdc\x89\x39\x9b\x85\xa3\x38\x43\x84\x24\xdd\x85\x32\x79\x0f\x50\xb7\x80\x42\xc8\xc7\xb3\x7e\x19\xc8\x20\x3e\x71\x98\x67\xd2\xde\x06\x1f\xf2\x9d\x8a\xc9\x68\x67\xd2\x2e\x26\x97\x58\x57\x0a\x00\xa6\x0c\x32\x7b\x3d\x07\xdb\xfd\xf0\x1a\xd8\x76\x11\xb6\xbf\x6f\x00\x13\x3f\x65\x83\xbc\x9a\x53\xc7\x57\x54\x63\xa8\x5b\x26\x1b\xe5\x13\x0e\xa4\xc5\xd6\xdd\xcf\xa8\x43\xf8\x99\x36\x61\x68\x63\x03\xb5\xe6\x4d\xda\x77\x37\xb4\xf6\x3e\x3b\x46\xdc\xb5\x66\x0c\x5a\x67\x43\x72\x86\x7e\x27\xb2\x84\xb9\x88\xe6\x72\x73\x59\xa6\x2b\x66\x33\x61\x74\xf9\xde\xc2\x69\x8c\xd7\x6e\x66\x43\x8a\xe6\xfc\x46\x3c\xe5\x2c\x87\xbf\x72\x70\x1d\x99\x61\x31\x3e\xba\x2c\xea\xd8\x88\x17\x8e\x8c\xbc\x99\x7f\x15\x10\x8d\x93\x9d\x3c\x2c\x67\x6a\x59\xc1\xcd\x43\xfc\x0d\x6a\x81\x27\x0b\x7d\x28\xa2\x7d\x75\x2e\x4e\x39\x04\x26\x69\x2e\xd8\x91\x02\x60\xaa\xd0\xad\xae\x21\x42\x8a\xaa\x70\xed\x58\x4a\x67\xe8\x77\xf7\xe2\x74\xfc\xa9\xc2\xb7\x7d\x05\xea\x08\x34\x4f\xd5\xa5\x68\x9f\x03\xa7\x24\xeb\x49\xd3\x83\xa3\x41\x72\x33\xa5\xa6\xb1\xb2\x9c\xb7\xef\xa1\x78\x38\x4c\x71\x66\xcc\x0c\x5d\x23\x41\xdc\x13\xf5\xf2\xc2\x9e\xb9\x57\x7b\xf9\x09\x31\xff\x59\xcf\x7f\x36\xf2\x9f\x4d\x0f\x58\x8c\x7c\xca\x50\x70\x0d\xf0\xa2\xb8\x12\xae\x79\xe5\x4f\x51\x23\x0a\x40\xf6\x6c\x65\x23\x87\x10\x43\xe8\x7b\xff\x94\x82\x21\xf2\x8b\x3e\xa4\xca\x37\xb5\x6c\xb3\xa0\x6c\xd3\x7a\x24\x2a\x33\x84\x2a\xad\x7a\x2a\x81\xaa\x8f\x75\xf5\xb1\xa1\x3e\x36\x3d\xa1\xb0\x30\x36\xef\xd5\x55\xb4\x47\x4e\xbe\xdf\xc5\x18\xd9\x27\x5d\x19\x26\xeb\xac\x7b\xe8\x7e\xe4\x66\x23\x1a\x76\x20\x28\x2d\x59\x5b\x06\xf6\x1d\x66\xc1\x42\xe1\x46\x92\x8a\xea\x04\x53\x8b\x8e\xab\x26\x0d\xd6\x19\xbc\xfe\x5d\x61\xb6\x35\x9b\x06\x28\xad\xeb\xd3\xa1\xd5\x32\xe6\x07\x6a\x35\xd4\x5a\x0d\xbd\x96\x55\xdb\x94\x36\xf5\xe9\xd4\x6a\x35\x6d\x6a\xa8\xf7\xda\xd9\xc1\x7e\xf4\x97\xb7\x40\xdb\x89\xe1\xc8\x72\xc6\x11\xfb\x2f\x1d\xd5\x0d\x54\x7f\xcd\x7e\xbe\xe1\x33\xc4\x5e\x38\xf6\x5d\x98\xe3\x70\x98\x01\xa5\x7b\x0e\x45\x59\xe1\xc4\x71\xd4\x33\x32\x79\x92\xba\xa6\x26\x24\xaf\xdf\x25\x45\x57\x25\xad\x1b\x72\xd7\xef\x92\x52\xab\x92\x36\x74\xa9\xeb\x77\x49\x7f\x95\x36\xa5\xd7\xc6\x36\xbc\xb4\x64\xdb\x00\x00\xb9\xba\x8a\x5c\xdd\x81\x5c\x63\x0e\x72\xcd\x42\xe4\x6a\x77\x44\xae\xa1\x22\xd7\x70\x20\xd7\x9c\x83\x5c\xad\x10\xb9\xfa\x1d\x91\x6b\xaa\xc8\x35\x1d\xc8\xd5\xe6\x20\x57\x2f\x44\xae\x31\x17\x39\x2b\xe9\x7e\x9a\x82\x11\x51\x9a\xf9\x19\x36\x0b\x00\x3b\xc9\x6a\x96\x8e\x01\xcb\xc8\x74\x3d\x1a\x7c\x21\x73\x91\x35\x6c\x5f\xc8\x40\x64\xba\x76\xdc\xaa\x44\xb1\xae\xa7\x39\xbc\x0f\x96\x4f\x85\x9e\x3c\xa4\xb5\xa3\x9f\x5a\x2c\xcb\x47\x3f\xb6\x98\x2b\x48\x39\xb7\xe4\x4b\xa8\x5a\x8e\x12\xc4\xfa\xe1\xd8\xd5\xdd\xd8\x99\xeb\xc7\xc0\xce\x58\x42\x2a\x76\xb5\xbb\x60\xd7\x90\xb0\x6b\xb8\xb1\x33\x17\x90\x81\x9d\xb1\x86\x54\xec\xea\x77\xc1\xae\x29\x61\xd7\x74\x63\x67\xae\x20\x03\x3b\x63\x11\xa9\xd8\x35\xe6\x63\x67\x52\x2b\xe6\x91\xad\xed\x72\x09\xdd\x86\x2d\xeb\x48\x17\x72\x8c\xe5\xa4\x6e\xae\x96\x55\x65\x88\x3e\x4d\x97\xec\xc3\x8e\xc2\x5d\xd4\x68\x77\x56\x9b\x0d\xa6\x81\xae\xda\x54\xc1\x5c\x62\x11\x02\x52\xca\x3c\x87\x99\x6a\xf8\x45\xca\x12\x3e\x21\xc8\xe1\x3d\xf4\x07\x58\xe8\x88\x05\x90\xff\xc4\xd7\xfe\x64\x2a\x4e\xca\xf9\x07\x3e\xa7\x14\x56\x86\xaf\x33\xe9\x76\x7b\x65\x73\xfb\x78\x85\x9d\x23\x2a\x13\x6e\x92\xfe\x19\xdf\x78\x68\x30\xbc\x10\xd2\x7c\x0e\x65\x3a\xf6\x09\x12\xd7\x19\xd2\xa1\x30\x09\xbf\x92\xb7\x63\x03\xc4\x74\xda\x3d\x8b\x12\xfb\x9c\x86\x4d\xdd\xc5\xe3\x29\x4e\x2a\x9b\xdb\xf4\x5a\x9f\xea\xec\x9f\x3f\x63\x36\x2b\x72\x93\xaf\x9f\x3f\x87\x10\xb8\x60\x40\xa2\x58\x15\x74\xdb\x0d\x8f\xdb\x25\x74\xdb\x60\x3b\x22\x59\x26\x74\xdb\x2d\x2f\x37\x49\xe8\xb6\xc1\x87\x71\x12\xb4\x7f\xec\x76\xea\xb7\x67\x5e\xbb\x71\x2f\x6b\x91\x6f\x69\x26\xf2\x68\xc6\x1c\xdf\xd0\x2c\x83\xae\x84\x97\x88\x19\x50\x90\xe6\xd1\x20\x9e\x4c\xe3\x08\x62\xae\x93\x6f\xab\xcf\x9f\x89\x79\x1f\x87\xfd\x15\x56\xf4\xeb\x57\xd9\x00\x40\x78\x7d\x3e\xb0\x71\x87\x9f\xe2\xdc\xaa\xc3\x4f\xb1\xf4\xed\xd7\x38\x09\xc0\x2f\x5d\x14\x10\x6f\x64\x08\xb3\x21\x18\xfc\x01\xad\x6f\xf2\x5b\x9e\x1c\xa6\xf5\xb3\x82\x19\x06\xd7\xaa\x1e\x59\xa8\xd2\xfb\x4f\xd9\x70\x1d\xa0\xe0\x68\xb0\x42\x1e\x34\xac\x3b\x2d\xf1\x95\x3e\x16\x19\xa2\x88\x2f\xdb\x97\xd3\xf7\x5b\x3b\xf9\x65\x13\x7d\xb6\xde\x60\xf5\x53\x6a\x9f\x47\x96\x15\xbf\xc5\xca\xf0\x64\x3a\xf6\x33\x1b\x83\x12\x51\xa6\xff\x8c\x58\x44\x1e\xae\x41\x05\xaf\x02\xc1\xeb\x40\xef\x17\x7e\xc1\x2b\x3c\xc2\x64\x17\xb5\x50\xa5\xde\x58\x47\xfd\x30\x4b\xab\x45\x00\xc3\x4b\x0b\xbc\xbd\x5f\xee\x0a\xee\x7c\xfb\x63\xef\xfc\xb7\x9d\x83\xa3\xfd\xf3\xfd\x83\xad\x6d\xb4\x09\xb1\x0d\x32\x3f\xca\x50\x82\xa7\x09\x4e\x71\x94\x85\xd1\x05\x57\xc4\x10\x32\x9c\xc4\x41\xde\x77\x2b\xcc\xad\xed\x52\x30\x19\x3b\x35\x60\x4a\x97\x82\x9a\xc9\x91\x78\xb4\x53\x94\xe5\x92\x30\x9f\x4d\x8a\x6e\x0f\xfc\xbe\x67\x09\x18\x3c\x88\x24\x1f\x72\x11\xa5\xb8\xd4\x3b\x41\xf7\x64\x0e\xd0\xc9\x08\x93\x51\xcf\x62\x34\x63\x7e\x02\x84\x05\x20\x52\x18\x40\x2b\x20\x57\xf3\x87\xc1\xf0\xa2\x0b\xa4\xcb\x71\xad\xca\x3b\xaa\x81\x2d\x6c\x17\x29\x85\xcd\xc8\x2f\x8c\x5c\x93\x61\x43\x9f\xda\x63\x4a\xb8\x13\xd2\x23\xc8\x7f\xc6\x37\x2b\xd6\xb2\xdc\x35\x74\x30\xbc\x40\x95\x03\x68\xc5\x1f\x57\xa1\xce\xc0\x36\x78\x25\xc7\x40\x6d\x8b\x07\x12\xa5\x13\x7a\x4b\x48\x84\xf7\x8e\x10\xca\xa0\xa8\x4f\xe4\x5c\x11\x0e\xdc\xdf\x55\x29\xc1\x2c\x80\x14\x69\x41\xde\xe3\xf9\xd5\xf3\x0a\xdd\xa6\xb7\xe9\x30\xc7\x49\x85\x5d\x9e\xc1\x10\x7a\xe8\x4f\x14\x5e\x76\x51\x78\x99\xf3\xc6\x5b\xc5\xf4\x40\x99\x6f\x15\x52\x57\x89\x0b\xc5\x24\x07\x5d\x03\x20\x67\x0e\xa1\xf5\xd9\x8d\xb3\xba\x56\x2d\xb2\x87\x2e\xa1\x95\xa4\x27\xc7\x42\x7c\xa2\xa7\x87\xa5\xa7\x2d\xfc\x50\xf4\x24\x20\xdd\x8f\x9e\x54\x3e\x7d\x07\x7a\xda\x8b\xc2\x2c\xf4\xc7\xe1\x17\x9c\x22\x1f\x45\xf8\x6a\x7c\xc3\x30\x0c\xd8\x70\xcc\xa7\x25\xbe\x6b\x5c\x0f\xe3\x64\xb2\x1f\x07\x18\x6d\x53\x67\x35\x88\xd3\x9c\x73\xba\x38\x91\xe9\x14\xac\xab\xc1\xcf\x8f\x53\xad\xd8\x64\xec\x64\xf8\xdd\x91\xec\x83\x91\x55\xc5\xfc\x60\xe3\x14\x77\x24\xb8\x30\x0a\x15\x0b\x1b\x31\x4d\x12\xb9\x58\x54\xd4\x9b\xd3\x29\xa1\x05\x18\x2d\x9e\x6e\x3a\xb5\x5c\x33\x90\x21\xde\x10\x3f\xf9\xa6\x48\x69\xd0\x3c\x15\x67\x44\x72\xa6\x86\xf5\x71\x32\xa1\xd3\xee\xdb\x74\x37\x94\xbe\x73\x92\xda\xc8\xc9\xeb\xb5\xad\x24\xb5\xa3\x01\x5b\x19\xeb\x59\x3c\xa4\x84\x4e\x3d\x00\x6c\xfd\x00\xfb\xa2\x4a\xe9\x85\x03\x36\x3a\x2a\x1f\x86\x60\x0e\xa9\x68\x09\xb4\x67\xf7\x24\x1f\xb6\x04\x4d\xdc\x94\x19\x4e\xca\x18\x51\x51\xa3\xa2\xc0\xcf\x7c\xd4\x07\xd9\x4b\x2d\xe1\x90\xc7\x00\x34\x4d\x75\xc1\xdd\x9d\x75\xc0\x87\x38\x81\xb9\x1c\xc4\xd1\x20\xc1\x19\x5e\x66\xc3\x31\x8e\x2f\x14\xa6\x2c\xdd\x4b\x1d\x2d\x36\xd6\x10\x50\x03\x30\xa7\xfe\x2d\x8c\xa7\xe0\x40\x62\x29\x38\x58\x60\xd3\xfb\x9a\x32\x57\x18\x02\x94\x29\x3b\x09\x6f\xe0\x6d\xb0\x06\x24\xf0\x25\x76\x2e\x89\x3f\x09\x58\x34\x6a\x16\x8b\x46\x10\x46\x17\x0f\xc0\x4d\xf2\xce\x6f\x70\xf2\x60\xf0\x2b\x2f\x48\x9b\x2f\x54\x32\x29\x53\xef\x8a\x63\xee\xa4\x30\x56\xb2\xab\x85\x79\xa5\x43\xe7\xe0\x1e\x38\x0a\x6c\xb3\xef\xc3\x17\xb9\xba\x8d\xa6\x68\x7b\xc8\xbf\xf4\xc3\xb1\xdf\x1f\x63\x6a\x86\x98\xba\xb7\xc5\x73\xde\x99\xd2\x54\xb5\x13\x46\x6c\xe3\x2b\xdc\xa7\x18\x5c\x75\x9f\xf9\x18\x67\xcc\x3d\x9a\x46\x4d\xa3\x90\xf2\x5d\x03\x85\x29\xc2\xc3\x21\x1e\x64\xe1\x25\x1e\xdf\x20\x1f\x05\x38\xcd\x92\x19\x3c\x7b\x28\xc1\x7e\xb0\x1c\x47\x03\x5c\x6a\x9f\x29\x4b\xbd\x80\xc6\x63\xd1\x30\x05\xfe\xd8\x94\xcc\x47\xb2\x52\x9e\x88\x45\x95\x45\xa9\x5f\x54\x9c\x4f\xfe\xbc\x68\x79\xfa\xdf\xc9\xe7\x62\x06\x85\xd4\x12\xe1\xb0\x10\x00\x2a\x5c\x2d\x4a\x51\xcb\x45\xc9\x02\x0c\x19\x02\x22\x11\x54\xd9\x82\xc3\x01\x0b\x98\xc9\x39\xf5\x8e\x34\x21\xd6\xc5\x67\xd6\x9e\xab\x6c\xae\x37\xd6\x57\x9b\x0d\xf9\x13\x55\x89\xd8\xbe\x68\x72\x50\x17\xd5\x95\xaf\xaa\xfc\xdb\x45\x8d\x32\x67\xa7\xd4\xaa\xca\xf6\xe7\x2b\xb2\x91\x73\x6d\xf2\x53\x0b\x1b\xe9\x93\x11\x96\x84\x02\x96\x69\xcb\x47\x23\xd0\x1a\x13\x21\xb3\xc4\x52\xe4\x22\xec\x66\xc4\xf1\x81\x08\x03\x7c\x59\x13\xa1\x89\xad\x6b\x4b\x87\xbe\xc1\x61\x89\x59\x7b\x9b\x2a\x4f\x4d\x47\x6e\xc8\xb6\xce\x55\xa6\xd4\xeb\x3a\xfd\xa6\xc8\x9f\xf8\x94\xe2\x31\x1e\x64\xb4\xe1\xe3\x2c\xf1\x33\x7c\x71\x53\x71\x99\x6b\x4b\xda\x67\x10\x17\x37\xd0\x0b\xca\x4a\x5f\x38\xcd\xc3\xd8\x6c\x1c\xfa\x69\x4a\xd8\xc4\x5b\x3f\xc5\x81\xe2\x31\x27\xff\x15\x1b\x87\x31\x50\xc7\x38\x81\x03\x17\xd9\xd5\xdc\x90\x8a\x17\xb9\x9e\xdc\x8f\xdd\x67\x14\xd8\xa8\xbb\x90\x62\xe4\x24\x33\x36\xf3\x86\xa5\xc8\x6e\x34\x8f\x02\x66\x9f\x07\x71\x71\x43\x51\xf4\x90\xfb\x02\x47\x1f\x03\xcf\x61\xe9\xc9\xc8\xbe\x6b\xf4\x5f\xbb\xcf\xb9\x17\xda\xea\x4d\x91\x87\x0a\x6f\x8c\x74\xcc\x2d\x13\xaa\xb3\x6d\x99\x4b\x56\xaa\x4c\xc3\x6b\xbf\x7a\x53\x75\xd8\x69\x96\x60\x7f\x72\x27\x55\x36\xc8\x50\x4c\xf9\x2c\xdb\xe0\x37\x1b\xcb\xfd\x90\x1a\x6c\xab\x27\x1a\x2a\x9d\x40\x1c\x6b\x49\x33\x5d\x47\x95\x66\x43\x55\x4c\x4b\x0a\xdf\x63\xc0\x4f\x53\xfb\xea\x2f\x0b\x3c\x42\x76\x2c\x7b\xad\x6d\x87\xe5\x22\xe2\xd4\x4f\xe0\xb8\x65\x13\x10\xcd\xed\x0d\x8e\x37\xb9\x75\x15\x17\x1a\x7f\xf8\xe1\xc5\x70\x3c\x4b\x47\x2f\xca\x6d\x73\x14\x8a\x6b\xa3\x13\xc3\xdc\x45\xf5\xa2\x79\x85\x73\x2d\xa4\x35\x9d\xca\xb7\xa5\xb2\xf2\xfc\x7c\x42\xcf\xbe\xbd\x15\xf6\xe3\xcf\xdb\xf9\x14\xa2\x78\xec\x40\x3d\x83\x4a\xa4\x36\xa4\xdb\x4d\x76\xd0\x36\x9c\x83\xd9\x7b\x59\xe9\x5d\xa4\xa0\x97\x55\x94\x13\x9e\x9d\x2b\x97\xaf\x17\xde\x4d\x37\xd5\x1e\x59\x15\x82\x7a\x6a\x99\x5c\xc1\x0f\x54\xfd\x0d\xf6\x43\x3e\x53\x7c\xbb\x03\x3d\x6c\xef\x6d\xcf\x50\x45\x73\x8e\x12\x5e\x52\xaf\x9d\xbb\x68\x9e\x73\x18\x85\xba\x42\x51\x97\x2b\x9a\xa4\x7a\x77\xd2\x38\x8b\xe9\xcc\x0f\x48\xff\x33\xa7\x33\xd7\x04\x2f\x38\x9d\x56\xc5\x6f\xc9\xe9\x14\x75\xef\x31\x9d\x45\x0a\xdf\x72\x57\x07\xdf\x74\x3a\xef\x3d\x5d\x05\x4b\x60\xce\x7c\xe9\x7a\xd3\x82\x49\xa2\x9b\x89\xd0\xf3\x0e\x6c\x62\x1d\xb3\xba\xbe\x44\x1b\x28\xbc\x94\x67\xab\x68\x8b\x60\x3b\x26\x0d\x2c\xdd\x1b\xf9\x61\x04\x39\x4f\x5c\x77\xad\x6f\xc1\x6e\xe0\x9c\x77\x1e\x6d\xb8\x83\x0f\xe8\x2a\x36\x65\x07\x21\x75\x0d\x62\x90\x86\x26\x6f\x4c\xdb\x25\xc4\x9d\xe8\xeb\x22\x8e\xf2\xb6\xc7\xb7\x03\xed\x24\x24\x35\xa1\xcc\x1d\xe9\xd5\xdb\x9e\x65\xef\x31\xc1\xd3\x26\x0e\x45\xfc\xcf\x8c\xab\x31\x28\x95\xfa\x19\x33\xea\x5e\xd1\xeb\x18\x30\x34\x9a\xa5\xd2\x91\xd0\x8a\x30\x61\x29\xe6\x32\x12\x52\x39\x21\xb2\xde\x90\x30\xbb\x2c\x02\x84\xfd\xbc\x1a\x61\x16\x7a\x9f\xe2\x07\x91\x3c\xd3\x12\xc8\x99\x0b\xc3\x5e\x90\xfc\xc1\x54\x32\x51\x87\x7a\x03\x40\x7e\x3c\xe8\x82\x70\x6d\xd0\x65\x59\x79\x32\x50\xae\x02\x34\xcc\xe4\x55\x28\x4e\x5b\x68\xab\x03\x2c\xd2\x6f\x48\xe4\x85\xe4\x30\x9c\xcd\x85\x58\xa1\xc9\x11\xaf\x1c\xe6\xac\xbf\x1d\x1c\xc1\x79\x99\x11\x9d\x59\xe6\x3a\x4e\xa0\x5f\xb9\xa2\xdb\x43\x4a\xbf\xbc\xbc\x59\x9b\xd0\xcf\xf0\x90\x7d\x5d\x2a\xfa\xe8\x5a\x31\x3b\xc2\x13\x0c\x52\x38\xec\xae\x94\x04\xd8\x55\x14\x9c\xf6\xc1\xa1\x1d\x5e\x9b\xd5\xb9\x04\x8b\x2f\x79\xdc\x79\xca\x4c\x69\x42\x79\x8e\xb7\x30\x05\x74\x76\x40\xf6\xdc\x99\xbb\x6e\x03\x5c\x62\xdd\x8a\x7d\xea\x69\xdd\x3e\xad\x5b\x74\xf7\x75\x7b\x9f\xd5\x01\x16\xc2\xa3\x30\x5d\x78\x6d\x58\x31\x61\x14\x0d\x5c\xe4\xb7\x83\x23\x27\x07\x90\x3d\xc8\x0c\x0e\x70\x5f\xb6\x63\xc5\xec\x24\x1f\x9a\x3e\x1e\xc4\x13\xb6\x74\x08\x5b\x08\xe3\x59\x5a\x9e\x79\x88\xc1\x2a\xcb\x1e\x04\x29\xf1\x6e\x54\x9c\xb8\x2f\xe4\x01\x05\x22\x12\x97\x96\x6c\x1e\xfe\xa3\x38\x4e\x31\x9a\x84\xd7\x44\x16\xb2\xf4\x0f\x3c\x41\x4d\x21\x0d\xc9\x84\xc8\xa4\x30\x17\xd9\xc5\x97\x20\x9d\x92\x93\x4e\x3a\xeb\xa7\xf8\xbf\x67\x38\xca\xac\x2a\x06\xa4\x8a\x76\x52\x5a\x0f\x75\x14\x9d\xaa\x41\x19\x25\x6d\x56\xe6\xab\xfa\xc9\xce\x66\xc3\xca\x16\x23\x29\x5f\x6d\xd6\x48\x49\xe4\x0f\x26\x30\xb7\x1e\x0f\xcf\xd0\xef\x1b\xb4\xde\x69\x58\x18\xba\x24\xff\xcd\x4d\xa0\xdf\xf6\x58\x79\x25\xa0\x89\x24\xda\x1e\xfa\x41\x40\x26\x70\x8e\x02\x64\x0a\x69\xae\x7a\x2b\xf4\xbf\x76\xf5\xc7\xe1\xfb\xde\x31\xfa\x5f\xed\xd5\x35\x34\x65\x40\x53\xa6\xcb\xb3\xc1\x3c\xfc\x3c\x48\xd7\x40\x4e\x9e\xfa\xc1\x0a\x7f\x2a\x90\x8d\x0f\x7d\x7e\xfd\x3c\x4b\x79\xec\x7c\x11\x08\x85\x99\x2b\x43\xe0\x64\x81\xc7\x42\xf6\x57\x00\x59\xbe\x7d\x26\x68\x59\x2b\xd9\xf5\x78\x2c\x04\x94\x74\x1f\x09\x80\x52\x11\xcd\x92\x0c\x0a\xc4\xb3\x7c\xe4\x63\xb3\x38\x7c\x89\x71\x25\xbf\xf2\xeb\x35\x4f\x8b\x9b\xa5\x5c\x30\xfb\x81\x7e\xb9\x76\x67\x06\x22\xaa\xd1\x58\x27\x1b\xd2\x78\xb9\x62\x86\xcc\xa2\x4c\xd0\x0e\xf8\x15\x99\x50\x23\x46\xb0\x06\x50\xfa\x62\x99\xe6\x9c\x16\x11\x56\xfe\xa5\x15\xb0\x35\x4b\xef\x85\x78\xbb\x66\xe8\x05\x9a\xea\x0d\xbe\x12\x7a\x81\x08\x28\x0a\x16\xb9\xaf\x8b\xf1\x9e\x39\xb8\x18\xef\xc1\xad\x45\x79\x3b\x17\xb3\x42\xa4\xd2\xe2\xf0\x05\x39\xfb\x51\xdb\x44\x21\x5a\x72\xb9\xe5\xcb\xd0\x69\x9c\x7b\xe9\x4d\x81\xf4\xaa\x61\x87\x36\x72\xdb\x77\x7e\xf8\x97\x41\x7b\x2a\x4a\x36\x33\x84\xcd\x20\xb0\x0f\x02\xcc\xf5\x20\x8e\x06\x7e\xc6\x61\x96\xd6\xc0\x7c\x8a\xa6\x82\xa1\xc0\x92\x1d\xf9\x01\x0d\x64\xc4\x16\xea\xb7\xe1\x32\xb3\x48\xe7\x33\xdf\x84\x23\x40\xb3\x25\xae\xdc\xa1\x9c\xce\x12\x6c\x7c\xe0\x1d\xce\x94\xcc\xc5\xd2\x22\x86\x18\xb0\x68\xec\xa7\x19\x3c\xcf\x5f\xd3\xb9\x78\x7d\x5a\x51\x97\xf3\x32\xaa\x57\xa9\x8b\xd9\x19\x73\x06\xb3\x79\x12\x53\xc1\xc1\x4d\x31\x39\xb8\x0d\x7d\x0d\x4a\x9b\x29\xdd\x36\x17\xd4\xf3\xff\x15\x17\x41\x36\x17\x05\xfb\xcd\x82\xed\x56\xa1\xe8\x1e\xe8\xe1\x8c\xfe\xf7\xe3\x00\xdf\x52\xf5\xe0\x89\x38\xad\xd1\x4b\x11\x38\x49\x48\xdd\xe9\xbd\xed\xb9\xa0\xb0\xb9\xba\x15\xf4\x45\x60\xe9\xc2\x86\x09\x11\x48\xde\x41\xe0\xe0\x47\xc0\x06\x40\x32\x9c\xd4\x08\x9c\x60\x0a\x98\x79\xda\xa9\x8e\xb6\x6d\x34\x71\xab\x78\x23\x2c\x60\x18\x48\x27\x5a\xfd\xd8\x93\xac\x0f\x8b\x6d\x00\x0b\x02\x9c\xa9\xf6\xa1\x16\x3f\x4e\x90\x9b\xc9\x08\x28\x6a\x51\xa4\x2a\x76\xc9\xf7\x09\xd8\x7e\x3a\xf0\xcf\x27\xd6\x3c\x0c\x18\xb6\xa4\x5c\xd2\x56\x8d\x4b\x9c\x27\x06\x02\x15\xb6\x44\xd0\x68\xc0\xa9\x5c\xbb\x9b\xb1\x4b\xfb\xab\x2f\x8b\x9b\x57\xad\x57\xaa\xe8\xe5\xea\xc2\x18\x08\x55\x8b\xe3\x2c\xf3\x1e\xe3\x29\xf2\x33\x34\xc6\x84\x0b\xc6\x11\x5f\x01\x2c\xcd\x07\xb5\x04\x85\xfd\x1a\x18\xae\xc9\xb7\x90\x38\xdf\x4c\xc2\x88\x1a\x89\xb2\x43\xbc\x11\x2e\x51\x7d\x64\x95\xe8\xf4\x49\xf8\x53\x42\x9a\x80\xfd\x31\x3d\xf2\x86\x97\xe8\xa7\x9f\xac\xfa\x78\x3d\x50\xc7\xe1\x9d\x74\x19\x39\x26\xaa\x32\xc5\x79\x3e\xd7\x9b\x2d\x7b\x25\xed\x16\x49\x73\x91\x44\x18\x4a\xb3\x57\x16\x82\xe6\xcd\x3d\x2c\x21\xaf\xae\x92\x83\x0c\xcd\xf7\xe5\x12\xb9\x40\x5e\x67\xa6\x5f\x20\x81\xc3\xef\xb9\x3a\x08\x7e\x15\x4f\x6d\x04\x5d\xa7\xe4\x3b\x5d\xc6\x3f\xde\xb2\x7a\x5c\xbc\xad\xed\x81\xe4\x37\x67\x06\xa8\x7c\x64\x6b\x6f\x9e\xe5\xdf\x3d\x2d\x15\xc0\xf4\x8e\xc9\x1e\x76\x33\x14\x34\x88\xc7\x63\x4c\xe9\x3f\x1e\x72\xd1\x00\x44\x4d\x0c\xc9\xf4\x8a\x44\x0f\x49\x14\x95\x9c\xbc\xc9\x36\x9a\xf8\x57\xd2\x2b\xab\x5f\xa2\xdd\xf5\x83\x3a\xa0\x0b\x21\xa5\x4c\xed\xfc\xe2\x11\x52\x3c\x30\x2e\x48\xeb\x93\xf5\x69\x98\xe3\xba\x00\xa5\xfe\x98\x62\x0f\x3f\x00\x18\xa8\x24\x7d\x1a\x7e\x14\x27\xe1\x25\x95\x55\x38\xc7\xb0\x02\xe4\x57\xa9\xb9\x9c\x2f\x59\x0e\x9a\xb1\x56\xcb\xc9\x35\x77\xe9\x59\xb1\x7c\x33\x18\xe1\xc9\xdd\xe0\xda\x05\x4e\xa6\x32\x07\x8b\xe9\xa1\x04\xcf\x0a\x82\x26\x65\xbc\xcd\x93\x36\xd2\x53\x0c\x15\xb1\xf8\x5b\x5d\x0c\x1b\xc4\xd1\x25\x4e\x32\x45\x86\xa5\xe9\xee\xb8\x31\x25\x58\x7c\x52\xeb\x3f\xb7\xdb\xea\x21\xad\xa2\x3a\xaf\x8a\x97\x25\xed\x61\xe6\xbb\x58\xa9\xa8\xcd\x3f\xd6\x09\xef\x26\x19\x1f\xcd\x4e\xd4\x8f\x44\x16\xab\x69\x9c\xa6\x61\x7f\x8c\xdd\x2b\xd6\xd2\xd4\x62\xce\x4d\xf9\x40\x99\xf6\xa0\xf4\x1b\x3f\x81\xff\x69\x40\x41\x42\x7d\x4e\x56\x70\x57\xfa\x9d\x3b\x3c\x59\x2b\x7d\xc6\x37\x5d\xd5\x2f\xca\x5a\x4c\xf3\x94\xb2\x17\x22\xcb\xb8\x0b\xff\xce\x29\x28\x56\x65\xd7\x74\xe7\xb2\xd7\x60\x22\xbc\x6e\x99\x60\x2f\x2c\xe4\x7a\xf5\xe8\xfc\xbe\x77\xbc\x66\xaf\x20\xb1\xf0\x96\xbd\x84\x58\x38\x12\x50\xfa\x6e\xe5\x60\x8a\xa3\xe3\xe3\x0f\x46\xb5\xf2\xce\x64\xf2\xf4\xdb\x05\xaf\x49\x78\xbd\x17\xa9\xe5\x4a\x9b\x1e\xd1\x55\x9c\x2e\xb6\x8c\x91\x73\xdd\x98\xac\x44\xf3\x0d\x74\x70\x13\x72\xa8\x73\x03\xe7\x06\xb6\xdc\x2b\x03\x76\x05\xf8\x1d\x0e\x43\x7d\x8d\x17\xc0\x81\x2c\x60\x29\x4d\x01\x06\xe9\xe3\x70\xee\x45\x99\x63\x1c\xc5\xf4\x8d\xc6\x00\x59\xd2\x7e\x5c\xc4\x3d\xca\x2e\x69\x8a\xbc\xb8\xa6\x63\x6b\x7b\x09\xbd\x78\x61\xf7\xad\xb0\x96\x5f\xc9\x62\x9a\x6f\xc8\xe5\xca\x31\xa7\x96\x83\x54\x9d\x84\xc9\x2b\xca\xc4\x29\xc6\xc6\x65\x55\x95\x97\x40\x5f\xbf\x52\x72\xcd\xeb\xac\xf0\x49\xbc\xe1\xc7\x5e\x43\x47\x63\x95\x93\x28\x95\xcd\xbb\xd7\xa0\xed\xc0\xd5\x86\xf8\x69\xbf\xdd\x60\x3d\xb7\x11\xa7\x0d\x34\x2b\x2e\x72\x19\xc3\xee\xa5\x0e\x62\xf1\x75\x87\x58\x75\xbe\x7b\xc9\x45\xbc\x99\xe5\x41\x3c\x99\xfa\x19\x6c\x2f\x65\x97\xa1\xbc\x2d\x68\x9b\x98\x24\xfe\x94\xdd\x13\x6d\xcb\xef\x2e\xc8\x3d\x94\xe1\x60\x4c\xdb\x3e\xe6\xe4\xed\x20\x64\x89\xba\x5c\xbc\x51\xa1\x6f\x51\xbc\x34\xf7\x9d\xa3\x96\x91\x23\x2d\x29\x4b\x30\xff\x62\x0b\xd4\x48\xc4\x5d\xad\x02\x79\x67\x3b\xc6\x42\x7f\xcd\x43\x2c\x29\xee\x54\xb5\x5c\x49\xd1\x6a\x0c\xed\xfd\x69\xed\xba\xdd\xec\xd4\x3b\x83\x35\x48\x6c\xd0\x69\x77\x5a\xed\x61\x7b\x78\x56\xe5\xaa\x78\x00\xcd\x1f\xf2\x7e\x38\xce\x91\x25\x50\x70\x8e\x85\xe3\xf0\x25\xea\xe6\x8c\x8c\x86\xb5\x59\x7c\xcf\x2b\x5a\x63\xb2\xbf\xd2\xa2\xc2\x23\x5f\x27\x39\x9d\xde\x79\xc9\xa8\x31\x1b\xf8\x82\xbe\xc3\x1a\x7e\xd8\x00\x0e\xa6\x30\xaa\x2d\xbd\xa9\x9f\xa4\xb8\xa2\x2c\xd4\x82\x8b\xc9\x24\x55\x14\x3f\x79\x35\xab\x57\x02\x29\x8e\x68\x0c\xaf\x39\x8b\x8e\x12\x86\x81\x4c\x91\x7a\xb5\x08\x22\xbf\x8c\x93\x0e\xc3\x2c\x29\x84\x01\xee\x04\xa7\x19\xb5\x6d\xf0\xc7\x96\x05\xaa\xc1\x3c\xad\x9d\xa1\x8d\x0d\x94\xaf\x3d\xf4\xd3\x4f\x7a\xbb\xa7\x75\x56\x86\xaf\x49\x97\x0a\x6a\xfb\x9a\x5e\x60\x98\x2d\x23\x95\xc3\x18\x8b\x5f\x6b\x91\x99\xf2\x34\x3c\xd4\xaa\x16\x58\xd7\xc5\x97\xec\x88\x0e\x57\x41\x39\x0c\xb3\xbc\x01\x7f\x0a\x0d\xd4\xf4\x5b\x6b\xa3\xb8\x72\xab\x53\xef\x94\x63\x14\xd6\xa3\x91\xe3\x18\xe4\x49\xa7\x13\x55\x34\x2f\xbc\x2b\xe2\x8b\xf0\x2a\xf1\xa7\x53\x90\x23\xfd\x8c\x35\x2f\xab\x4c\x90\x4f\x76\xfa\x54\xf2\x4a\x2b\x5c\xbd\x8a\xab\x8f\xe1\xca\x96\x3b\xfc\xd8\x3e\x95\x75\x20\xb9\xf3\x65\x8f\x10\x7a\xb8\x8c\x5f\x24\xd5\x73\x1d\x81\xdc\x5b\xd6\x59\xea\x10\x1a\x05\x94\x6a\xc4\x01\x23\xbf\xd8\xb1\x1c\x9c\x8a\x42\x44\xe9\xde\x8b\x80\x50\xd7\x10\xd5\xa4\x89\x2d\x0c\x2a\xc5\xae\x1d\xc8\xbc\x31\x6f\xba\xfb\x78\xa8\xe6\xca\x27\xcb\x51\xa7\xc0\xfb\x9c\x35\x4d\x6d\x50\xd8\xef\xdc\xef\xfc\x6f\x12\xc3\xc5\xbe\x85\x6d\xfe\xb5\x1b\x18\x59\x96\x76\x8d\x8a\xb9\xac\x84\x7f\xa5\xa9\x8d\x50\x5c\x2d\x1d\xa7\xb0\xc7\x6b\x30\x0f\x52\xa3\xab\x13\xbe\x69\xe3\x9e\x58\x6d\x0e\x69\xa0\x40\xd9\x61\x71\x8e\x75\x7b\xb1\xde\x2d\x84\xce\x42\xd1\x73\xb6\x6d\xf6\xeb\x52\x74\x83\x38\x77\x3e\xb1\x05\x40\xb3\xfa\xac\x1a\x62\x49\xee\x99\x21\x02\x24\xb0\xce\xde\x45\x32\xe9\x41\xff\x72\x98\x70\x05\x6c\x40\x61\xf6\x46\x84\xe3\x0a\xc7\x5c\xd7\x7e\x54\x7e\x3b\x2d\xda\xb4\x95\xfd\xd5\x2c\xc8\x55\x8b\x96\x4f\x84\xac\x44\xdf\x56\xc2\x4b\x4b\x11\x49\x47\xc8\xe8\xc5\x2c\x43\xb5\x82\x39\x20\xb8\x10\x35\x8b\x09\x7d\x60\x5e\x92\xbd\xb2\x14\x96\x74\x81\xba\x85\xb5\xa5\xb4\xa4\x17\x24\xa4\x37\xb4\x1c\xd7\x6e\x4b\x1f\x5b\xd8\x3d\x74\x2a\x26\x4e\x28\xbe\xe4\x6b\x19\xf4\x68\xdb\x93\x4c\x00\x62\x87\xd2\x2e\x9a\xa4\x47\xc8\xed\xfd\x77\xdc\xa7\xb4\x00\x2d\x22\xd2\xf1\x37\xd8\x9b\xf2\xa8\xca\xf3\xd9\x34\xf7\x9e\xb7\xb0\x69\x4e\x76\x2c\x8c\x82\xe4\x51\x7f\x67\x96\xfd\xd0\x28\xea\xfb\xd2\x03\x6e\x29\xce\xd8\x05\x8e\x08\x03\xdf\x60\x57\x61\x1a\x07\x49\xb5\x20\x2f\x26\x0d\xb0\xbc\x53\xb0\xdb\x6f\x38\xbf\xca\xc8\xe7\xdc\xc4\xd6\x1c\xe3\x14\xe6\x86\x21\x4f\x9e\xb2\x89\x29\x51\x17\xe9\xb0\xe4\x7b\x93\xc4\x64\x14\x85\x8f\x75\x9b\x10\x4d\x2c\xac\x8d\xb1\xb2\x35\x7d\xac\xd4\xfb\x17\xd0\x31\xf9\x69\x3a\x9b\xe0\x40\xbd\x4f\xf4\xc7\x09\xf6\x83\x1b\x69\xbf\x53\x0e\x64\xb3\x88\xa6\xad\x2c\x11\xd1\x6c\x31\xb6\x67\xe7\x5f\x0b\x1d\x9a\x08\xe3\x02\x13\xf5\x24\xc5\x0b\xf3\x7a\xb7\xbe\x68\x16\x2d\x0a\xeb\x2f\x94\xb8\x0d\x92\xa7\x2a\xa4\x03\x4e\x05\x48\x10\xbf\x9d\x07\x9c\x1b\x3a\x25\x79\xf5\xb0\xca\xb6\x54\xde\x2c\x76\x8d\xbc\x08\xe7\x84\xb0\xe1\x36\x21\x94\x3d\x99\x4b\x55\xbf\xd8\x40\x85\xda\x51\x06\xad\x40\x29\x6a\x68\x26\xac\x37\x24\xef\xed\x26\x12\xf3\xae\x4c\x3e\x07\x43\xb8\x2f\xa1\xff\x2d\xbe\x2c\x99\x67\x85\x61\x5e\x98\xbc\xa7\xd0\x49\x2b\xe5\xee\x49\xb6\x08\x78\xb8\xd3\x27\x8d\x91\xb5\xbc\xf7\x0b\x57\x18\x4c\x59\xbc\xa0\xf2\xea\x58\x5e\x83\x59\x5e\xb0\x07\x90\x53\x48\x33\x00\xb8\xd8\x2b\x24\x0f\x54\x8e\xa9\x6d\x45\x18\x31\x4b\x5e\x66\x07\xc0\x4c\x66\x2e\x70\x04\xc6\xbc\xc5\xd0\x44\x94\x72\x07\x30\x1a\x3a\xbb\x18\x96\xa9\x33\x00\x15\x96\x24\x24\x6d\xa2\x4e\x0b\x4c\x8e\xe1\x03\xb7\x9f\xdd\x1b\xa2\x78\x12\x12\x19\xc1\x43\x3e\xfd\x74\x15\x8e\xc7\xa8\x8f\x45\x83\x01\x4a\xfc\x28\x88\x27\xe3\x9b\x07\x3a\xdc\x53\xab\x09\x36\x4c\x1e\xda\xfb\xc5\x83\x29\x25\x8d\x7f\x03\x2e\x44\x27\x39\x30\x59\x90\x44\x8d\x2b\xf8\x1a\x0f\x66\x19\xae\xbc\xe0\xd1\xa8\x5e\x78\x2c\x71\x87\xc7\xcc\xb7\x1c\x62\xd1\x03\x41\xf7\xd0\x0b\x32\x1c\xe4\xff\x5f\xb8\xcf\xcc\x14\x8c\xcc\xdd\x38\x35\x7b\x9c\x44\x3d\x46\x5d\x54\xb1\x69\x37\xea\xa7\xd3\xcc\x66\xd9\xa1\xa8\xfe\xc1\x79\x95\x64\x28\x91\x29\x9c\x4a\xa7\xb5\x6a\xa4\x35\xb7\xb8\xd5\xd1\xa5\x2d\xad\x6b\x53\x5a\xa1\xf1\x66\x69\xe2\x81\x5c\x81\x2b\x62\xdc\xe5\x69\x90\xd9\x42\xba\xad\xae\xb0\x44\xde\xd2\x78\x00\xfe\xd6\x80\xb5\x84\x36\xb3\x62\x0c\xc0\x6e\xda\x50\x93\x8b\x64\xd0\x4c\x41\xce\x93\xc9\xf2\x31\x47\x2f\x4d\x7d\xb6\x92\x1a\x3a\x4f\xe1\x6c\x77\x96\x3a\x62\xa2\xd4\x82\x87\xf1\xfc\x48\x2d\xa4\xe8\xbb\x69\xb5\x6d\x9a\x01\x45\xc5\x1d\x30\xbe\xcc\x59\x9e\xc6\x92\x3d\x01\xcb\x21\x7e\xdd\x5d\x1f\x6e\x89\x12\x27\x14\xe2\xf6\x6f\x36\x0d\xd7\x23\xea\xc7\xdf\x6f\xed\xdc\x22\xb2\x7d\x72\x0b\x4a\xdb\x2e\x9c\x4b\x79\x9c\xd9\x16\x6f\x71\x0b\x69\xc5\x2d\x1d\x76\x3b\x3f\x7c\x0e\x86\x5d\x69\x7b\x96\x28\x64\x41\xf5\x38\x73\xa9\x5a\x64\x5f\xfe\x3e\xf4\xe5\x85\xd2\xc1\x77\xa0\x8e\xf8\x9b\xa8\xcd\x2d\x8b\xaf\x94\x26\xf9\x05\x1f\x6a\x57\x58\xd9\xc7\x6f\xd8\x43\x7f\x3e\xb2\x06\x3b\xdf\x8e\xbe\x91\xc2\x41\xdb\x5d\xe3\xcc\xa5\xdc\xb5\xc9\x2e\x04\x3c\x11\x5b\xb8\xb8\x22\x61\x4f\x87\x57\xc8\x18\xec\x99\x6e\x7b\x2e\xef\x4e\x2a\xc6\xd2\xbe\x19\x5d\x5a\x81\x2d\x56\xc1\x60\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\xbc\xa6\xc9\x40\xa0\x82\x75\x48\xa5\xb3\x35\x35\xcc\x54\xa0\xbb\xf4\x26\xd6\x13\xdf\x3d\xf4\xc1\x7f\x01\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\x92\x7a\xe2\x6f\xce\xbb\xbf\xb9\xb2\xf8\xdb\x6f\x15\x9e\x62\xdb\xe3\x38\xa1\x3d\xdc\xde\x51\x4a\x1f\xee\xbe\xbf\xb0\x6d\x1d\xf2\xf8\x96\xdc\x3d\x8a\x14\xe4\xb9\x2a\x4f\x64\xba\x94\x53\x5a\xb2\xfc\x95\xb7\x67\x5e\xbb\xf9\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\x73\xe0\xe2\xa7\x9a\x7c\x72\xdf\xcf\x46\x1e\xb2\xa4\xa0\xcc\x8f\xd7\x1f\xe2\x81\x3f\x46\xd3\x78\x7c\x33\x0c\xc7\x28\x1e\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\xff\x0f\x83\xa9\x81\x8d\xe0\xac\x4f\x66\x50\x27\xe2\x9d\x95\x69\x12\x67\x31\xf9\x84\x36\xc8\xe9\x43\x2f\xc0\xea\xa1\x0d\x14\xe1\x2b\x82\x40\x31\x84\x68\x36\x1e\x3b\x16\x8a\xc0\x20\x5f\x26\x52\xbc\x23\x5b\x24\x4f\x3e\x27\xc5\x4a\x6e\xa7\x62\xfb\x43\xd8\x4f\xfc\xe4\x66\x9e\x8e\x5c\xca\x0f\xea\x04\x05\xd9\x42\x99\xd6\x93\x08\x17\xbc\xcb\xfe\x18\x85\xd1\x08\x27\xa1\x12\xc0\x55\x89\xe8\xa0\xe7\x19\x35\x23\x8c\x9a\xd3\x59\x22\xec\x1f\x8f\x31\x0c\xee\x71\xc2\xcf\x60\xe4\x67\x1c\x21\x16\xca\x83\x8a\x41\xc6\xa9\x12\xa1\xa2\x38\x80\x5c\xee\x8a\x2f\x71\x92\x84\x01\x4e\xd1\x21\x55\x88\x84\x38\xa5\x0c\x7c\x7a\x83\xc2\x88\x65\x33\xce\x11\x28\xd1\x82\x9e\xab\xe1\x64\x51\x00\x86\xcc\xe5\x28\xb7\x48\xd4\x40\x32\x51\xfb\x37\x27\x94\x84\x15\xe9\xa6\xc0\x24\x51\xf6\x17\x0b\xf1\x38\xe8\xa2\x17\x90\x29\xeb\x85\x6e\x38\x62\x6f\x93\xfc\x4d\x70\x36\x8a\x83\x42\x1f\x79\xa9\xb4\x1e\x23\xdf\xe6\x78\x86\x90\x19\xce\x90\xa2\xaf\x18\x64\xf3\x79\x75\x06\x31\x9c\xfa\x57\x91\xf9\x45\x62\x24\x44\x58\xc8\xd3\xea\xb9\xcc\x89\x37\x67\x17\x13\x1c\x59\x4c\x87\xc9\x8e\x52\x8c\x05\xca\x99\x0f\x3b\x77\xe5\xe5\xad\xe9\x1f\xac\x08\x30\x33\x29\xee\xfa\x15\x0a\xc7\xd2\xc4\x8e\xd3\x0f\xbc\xc9\x91\x9f\x1e\x5c\x45\x8c\xec\x6f\x2a\x2f\x48\xcd\x17\x55\xe1\xf3\x44\x1e\x61\x13\xe4\xe5\xc9\x8b\xb9\xfd\xa0\xb5\x0a\xa7\xdb\x52\xeb\xff\x49\x67\x53\x22\x6a\x45\x61\xb6\xe2\x13\xe1\x94\x6d\x7d\x7e\x72\x31\x23\xa3\x6b\x1d\x0f\x64\xc9\xa0\x50\x30\x4e\xb9\xc7\x6d\xf2\x22\x45\x39\x47\x0f\xa9\x52\x98\x4f\x3a\x5d\xa5\x26\x04\xb9\x83\xca\x7e\xe0\xd8\x76\x10\x57\x8c\x0f\x71\x82\xa3\x01\x69\x00\xc6\x79\xaa\xaf\x57\x63\x18\x98\x5c\x6c\x03\xe8\xdc\x67\x90\x2d\x35\x86\x8d\xa9\x6e\xc3\x4a\x49\x65\xa6\x49\x55\xde\xb3\x88\x8e\x03\x4c\x20\x5d\xb5\x66\x08\xd4\x4d\x3e\x1f\x79\x06\x9b\x4a\x55\x5c\xc3\x11\x51\x1a\x42\xca\x01\x90\x4a\xf5\xef\xcc\x2b\x79\xc4\x72\xb4\xc1\xd8\x26\xbf\xb3\x98\xcb\x8b\x68\xb9\x62\x8e\x67\x36\x02\x4b\x2e\x8f\x93\x6d\xae\x5c\x1e\x41\x5d\x5a\x23\xfc\x9d\xba\x4e\x9c\x54\xc3\x8b\xdf\x85\x6c\x8a\xdc\xd5\x1d\x73\x85\x0e\x18\x33\x63\x49\x02\x80\xa4\xc0\x84\x3e\x08\x50\x1a\x4f\x30\x4d\x3d\x85\xae\x46\x38\x42\x37\xf1\x2c\x11\x66\xf6\x3e\x11\x67\x29\xf0\x07\x8e\x9d\x7b\xdf\x5d\x50\x77\x74\x2e\xda\xcb\x10\x65\x00\x2b\x2b\xe6\xc8\x88\xa1\xbf\xe3\x76\x37\x17\x8d\x52\x73\xda\x8b\xa7\x44\xd8\x99\xe6\x72\x0f\x93\x77\xee\x21\x4e\x49\xc0\x40\xc3\xa4\xc8\x54\x13\xd0\x44\x3e\xf0\x94\xb2\xd5\x49\xf7\xcf\xb2\xf2\xcb\x1d\xc7\x1d\x1a\x51\x2e\xb1\x45\xff\xac\x6b\x5c\x44\x3c\xe4\x97\x6d\x1f\xfd\x09\x18\x4d\xcc\xa9\x87\xd8\x56\x9d\x17\xd3\x37\x6b\x19\x60\xb5\x70\x8b\x25\xd3\x79\x2a\x17\x3f\x43\x1b\x52\xfb\xea\xa7\x05\x52\x17\x39\x36\xd9\x6d\x74\x15\x47\x2f\x32\x2a\x3f\x73\x77\x47\x29\x78\xe1\x38\x8e\xa7\xc8\xef\xc7\x97\x96\x6d\xb0\xb8\xcb\x2f\x38\xb4\x17\xee\x0e\x03\x17\x15\xad\xca\xfd\x14\x6f\x4b\xe4\xd5\x2a\xb5\x78\xc4\xe1\x04\x7a\x0a\xf6\x2f\x8b\xac\x1b\xdb\xc6\x37\x18\xc7\x11\x7e\x04\x8e\x07\x70\xd1\x46\xbe\x87\xc0\x8b\x12\x3b\x19\x29\x36\x77\x23\x93\x73\x91\xa8\xc2\x11\xe7\xa7\x56\x7b\x32\xfb\x19\xd9\x7a\xbb\x1f\x21\x1f\x3c\x6f\xb5\x58\x84\x85\x91\x85\x8c\x38\xef\xc5\x20\x6c\xe1\x69\x84\xf1\x83\x1a\x0e\x31\x0d\x2f\xa2\x70\x18\x0e\xfc\x28\x63\x01\x25\x43\xda\x7b\x00\x49\xdb\xb1\x1d\x93\x7f\x95\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\x79\x00\xa7\xb5\xeb\x5a\xad\x56\xaf\x35\x6a\x4d\x0f\xd5\xae\x6b\xad\x5a\xbb\xd6\xa9\xad\x9d\x3d\x1a\x60\x0f\x75\x4a\x87\x5e\x61\xe1\xeb\xf8\x8c\x18\x2b\xf6\x8a\x39\x04\xc3\x72\xe5\x0f\xf4\xbf\x5f\xbf\x42\xcc\x5e\x4d\xd4\x18\xa2\x8a\x98\xde\x1f\x36\x2c\x8a\x42\xf9\x0f\xa0\x4a\x46\x43\xfc\x67\x69\x63\x52\x1d\x00\x25\x8f\x31\x8e\x2e\xb2\x11\x35\x3d\x72\x72\x91\xf2\x31\x63\xf2\x85\xb2\x58\xa4\x98\xed\x68\x10\x07\x84\xde\x31\xfd\xa1\x93\x3b\xbc\x2e\x8e\xfd\x29\x08\x00\x47\x83\x95\x5d\x7c\xed\x6e\x73\x5e\x00\x99\x52\xab\x7d\xe1\xe0\x2e\x39\xb1\x96\x88\xec\x62\x89\x6b\x30\x2f\xac\x8b\xa5\x8a\x32\x24\x9f\xb2\xe1\xfa\x42\xd1\x5c\xd8\x54\x38\x63\xb9\xf0\xa9\xfa\xfa\x15\xed\xe2\xeb\xc2\xf0\x2d\x73\x08\x68\xe0\x67\x38\x62\x7b\xbe\x4a\x41\x0e\xe6\xef\x26\x24\xe9\x1e\x36\x1f\xf0\x13\xc6\x0d\x25\xca\x84\x34\xbf\x8b\xde\xeb\x96\xc5\xa5\x0c\x6d\x08\xec\xea\x3c\x7e\x86\x78\xd3\x70\xa7\x34\x83\x92\x3a\x53\xa2\x81\x9d\x17\x0b\x47\x42\x06\xf6\x57\x83\x61\x59\x7c\x15\xb3\x91\x2f\x42\x1d\xe4\x24\xe6\x2e\x1d\xa6\xc7\x39\x8f\x51\x78\x8e\x03\xf8\xb1\xca\x92\x28\xfc\xbc\x8e\xd1\xa9\xde\xd8\x9f\x4c\x11\xbe\x86\x48\x92\xfd\x50\xef\x1c\xbd\x57\x25\x65\xcc\xdb\x06\x7a\x9f\x3a\xb0\x05\x49\x51\x10\xff\x97\x23\x50\x3a\xd4\x27\x22\x69\x84\x61\xab\x45\x7e\x86\x7c\x94\x85\x13\x8b\xc4\x6d\x0b\xc9\x2e\x77\xd7\x9d\x14\x42\x1e\x1c\x52\x14\x6d\x10\xf4\xd8\x2c\x9c\x86\x3c\x2a\x36\xf9\x4f\xa5\xd1\x42\xcb\xa8\x12\x52\x8c\x5f\xa2\xf5\x6a\x55\x44\xcb\x76\x4a\xf1\x14\x8e\xda\xe3\x25\x14\x8a\x70\xdb\x5f\x37\xf2\xa6\xdf\xbc\xe1\x6d\x58\xca\x8b\x46\x4b\x08\xfe\xce\x6d\x49\x1e\x53\xba\xb8\xee\x35\xa6\xee\x28\xf7\x65\xbb\xbf\x81\xcc\xc1\x2e\x93\x31\xd8\xa4\x42\xb1\xd9\x2e\x6d\xa8\x68\xda\x72\xac\xf8\x61\xe4\xf7\xf5\x93\x87\x74\x00\x28\xcb\x4e\x69\x0c\x0e\x22\x04\x2a\x82\x61\x98\xdd\x57\x14\xcc\x17\xa7\x58\x5d\x0e\x26\x45\x3e\x97\x0d\xdd\x6b\x61\x4d\xa6\x1c\x65\x8b\x8b\xe4\x64\x32\x76\x86\x61\x11\xd5\x4e\x05\x0c\x1e\x67\x7e\x03\x96\x0e\xfd\x03\xd2\x6f\x36\x08\xe9\xa7\x0a\x5f\xb0\x10\xbc\x22\x4a\x6d\xa0\x7d\x3f\x1b\xad\x0c\x70\x38\xce\x6b\xae\xa2\x05\x22\x12\xd9\xcf\xbf\xa5\x76\x1e\x87\x39\x92\x71\xfc\xbd\xab\xdd\x27\x3b\xee\xca\xb4\x60\x9c\x77\x55\x5a\x98\x77\xce\x95\xc1\xc2\x49\x8d\xe2\x2a\x47\x3f\x37\x4f\xce\x2b\x26\x8d\x30\xf3\xfb\x9a\xd3\xa4\x8e\xd4\x5b\x7c\x0a\x24\xb1\x61\x18\x8e\xc7\x3c\xec\x2c\x73\x93\x80\xf3\xd6\x7c\xa1\x84\x1f\xe6\x22\xdb\xa1\x57\x06\xe5\x74\xf1\x29\x35\xcb\x0c\x52\x29\x42\x79\x28\xe3\xb3\x12\x47\x30\xe6\x0a\x52\x77\x9f\xb4\x68\x09\x99\x4c\x22\xfb\x11\x4b\x66\x0f\xe6\x81\x8a\x7c\x4d\xd4\x1b\xf2\xc9\xf9\x95\x3b\xca\xfc\xf9\x15\xda\x20\xff\x3a\x12\xa8\x4d\xce\xbf\x90\x6d\xe6\xba\xe9\x07\xb8\xb3\xde\xd7\xc3\xaf\x8b\x62\x7e\xfa\x19\xc9\x9c\xa3\xe0\x9e\xa0\xc4\xdd\x1d\x6d\xb5\x52\xbb\x7e\x55\xeb\xbc\x42\x2f\x49\x17\xbe\xc0\x9e\xbe\xb3\xb3\xb3\x53\x45\x4b\xf4\xc5\xcf\x3f\xa3\xda\x75\xbd\x06\xdb\x3d\x41\xc0\xb1\xdd\xd3\x2e\x56\x6a\xd7\xad\x4e\xbb\x46\x81\x5d\xe9\xc0\xae\xca\x02\x83\xe1\xc5\xe9\x0c\x3c\x7d\x2a\x80\xc6\x9b\x37\xb4\x26\x5a\x42\x30\xd2\x85\xf5\x59\xdd\xd5\x0d\xa8\xc3\xfe\x8a\xcb\x2e\x6d\xa0\xda\x4a\xdb\x59\x06\xc6\x94\x15\x7d\x49\xed\x6d\x38\xb5\x55\xd1\xcf\x68\xa5\x8d\xfe\x03\xd5\x51\x17\x2d\xd7\xcb\x88\x28\x06\xe7\x50\xc5\x0d\x0f\x25\x03\x7f\x30\xc2\x2c\xbb\xce\x7c\x81\x83\xd4\x3c\x27\xf4\x98\x54\x2a\xb4\x2a\x39\x2a\x29\x48\x92\xdd\x44\x1a\x0c\xfb\x15\x13\xad\xba\x81\xce\x93\x0a\x2d\x0f\x04\xb9\xd6\x5f\xb3\xf4\xe9\x2a\xcf\xe1\x53\x11\xe5\x73\xf8\xe8\x2b\xaa\x95\x0c\x6b\x1e\xe1\x2b\xc9\xd9\x09\x6e\x1d\x99\x02\x24\xe2\xe9\x7b\x9e\x69\x23\x69\x77\x3e\x65\x47\xfb\x79\x86\x34\x38\x1a\x80\x21\x0d\xfd\xaf\xdd\x90\x66\x17\x5f\x9b\x9a\x00\x1b\x38\x52\x70\x83\x02\x5d\xa1\xbf\xcb\xc5\xdf\xd4\xd5\x17\x23\x7c\x5d\x5a\x85\x51\xe2\xe4\xb9\x60\x54\xcd\x52\xad\x3f\x14\x23\x1f\xe1\x6b\x33\x84\x26\x1b\x3f\xe9\x68\x3f\x3f\x91\x90\x35\x70\xe6\x5d\x8f\xa9\x57\xa5\x4f\x9e\xe9\xa2\xc7\x48\x3a\xeb\x26\xa0\x11\xbe\xee\x8d\xfc\xa4\x74\x9e\xad\x74\xee\x81\x0e\x72\xa4\x85\xf4\x20\x77\x75\xcf\x43\x1c\xc7\x8e\xad\x71\x00\x4b\x80\xb4\xaa\xb9\xda\xa7\xde\xa9\xda\xf8\x9d\xad\x2a\x69\xa7\x36\x2c\xae\xeb\x60\x10\x02\xdc\x1f\x71\x18\x55\x5e\xbc\xb8\x43\xc4\x4d\x89\xc2\xe9\x7a\x5b\x44\xd3\xc3\x57\x0a\x25\xdc\xf2\x0b\xc6\x21\x3c\xfd\xf5\x52\x13\x5f\x6c\xd4\x66\x5b\xac\xc7\xf2\x91\x32\x69\x95\xc5\x12\xa5\xd0\x3a\x1f\xf8\xd1\x85\x3e\xb2\xa3\xcc\x22\xab\xe6\x6a\x91\xd4\x74\x72\xa3\x6c\x0b\x6d\x14\xe4\xc7\xa4\xab\xa5\x09\x9a\x09\xe8\xf4\x5e\x94\xb1\xce\xae\xa4\xb3\x7e\x9a\x25\x95\xd0\x43\x8d\xaa\x07\x49\xf8\x72\x95\x05\x59\x51\xeb\x55\x9b\x03\xee\xc2\x7b\x9e\x32\x4c\xab\xa8\x51\xd6\x7d\xf6\x83\x9f\x85\x51\xbd\xdc\xa6\xc5\xca\xf2\x7d\x4b\x3c\xde\x6d\xeb\x62\xd5\xff\xba\xdd\xab\x2c\x02\x0f\xb5\xa6\xc6\xd0\x9e\x7d\x0f\xa3\xb8\xfc\x8f\xda\xc6\xe8\x70\x7c\xc7\x3b\x99\x84\x20\xdd\x91\xe8\xd4\xad\x0c\x93\x78\x42\xde\xf6\xe2\x00\xc3\x26\x55\x76\x43\x92\x01\xde\x63\x4f\x52\xe8\xf6\xee\xdb\x92\x20\xc7\x85\x16\xc3\x77\xbd\x39\xb1\x55\x44\xf7\x27\x79\xb9\x95\xdf\xa2\x44\xad\xc5\x76\x29\x51\x4d\x6c\x54\xe2\xcd\x63\xef\x55\x5a\xd3\xf3\x72\x39\x87\x92\x16\x3d\xef\xed\xca\x80\x11\xf4\x66\x56\x09\xf9\x9a\xd0\xb7\x2a\xbb\x6e\x71\xe1\xad\x4a\x43\xb8\xec\x4e\xf5\xe9\x64\x67\x79\xbd\xdc\x46\xf5\x29\x1b\xae\x8b\x6d\x8a\x3d\xdc\x6d\x93\xa2\x8d\xfe\x75\x7b\x54\xc9\xf6\x1f\x6a\x65\xcd\xb2\xe1\xba\x7d\x83\x22\xa3\xf8\x98\xdb\x53\x96\xdc\x14\x18\x18\x05\x98\x1c\xd1\x3f\x1d\xed\xf5\xb8\xa7\x53\x05\xa7\x03\x7f\x8a\x2b\x05\x1b\xa7\xc9\x96\xd1\xc0\xcf\x06\x23\x54\x31\xd3\x47\x03\x0a\xa3\x24\xbe\x02\xba\x85\x8c\x2b\x95\x17\xfb\xfe\x78\x18\x27\x13\x1c\xb0\x69\x08\xfc\xcc\x37\x53\xd0\x2d\xce\xc0\xe5\x49\xbd\x3b\xff\x66\x73\xb5\x08\x99\x7c\xd7\xcc\x1b\x28\x8c\xb2\xee\x9c\x0c\xcb\x33\x6e\x56\xc7\x65\x0c\xa0\x6c\x0d\xb3\x88\x51\x0f\xb5\x10\x50\xe8\x8a\xc3\xa9\x96\x0e\x40\x23\x52\xf0\x42\x2e\x4c\x1c\xb0\x6c\x66\x92\x17\xba\x33\x13\xaf\x64\x27\x7b\x23\xa5\x44\x9b\xcc\xd2\x0c\xf5\x31\x0a\xc9\x88\x4e\x70\x94\xd1\x3c\x6b\x3e\x5c\xaf\x27\x38\x13\x1e\x0b\xa5\x72\xfb\x6a\x79\x3a\x55\xe5\x3e\xcd\x71\x48\x5d\xab\xf2\x04\xf1\x9f\xf1\x34\x43\xb3\x68\xca\x93\x06\xaa\xd9\x41\x25\x9b\x96\x9a\x85\xfb\xbe\x65\xe3\x00\x99\x06\x37\xc5\x28\x08\x2f\x31\xd7\xe7\x92\x66\x70\x90\xdd\x95\x59\xf3\x68\x23\xfd\x82\x25\xd1\x66\x49\x4c\xb3\x18\x85\x59\xca\xbd\x62\x10\xa1\xe0\xfb\xde\x31\xf5\xad\xc8\xd3\x84\xb8\xee\x4b\xa6\x52\x59\x77\x99\x79\x1f\x02\x2b\x65\x9b\xcd\x00\x64\xe0\x64\x9e\x8a\xda\xce\xaa\x33\x25\x5a\x3e\xda\xf2\x33\x9f\x0b\xeb\xb5\xb2\x92\xe6\x66\x10\xa4\xd0\x06\xcf\x0b\xee\x18\x69\x46\x0b\xe5\x37\x45\x11\x64\xc1\xc8\x3c\xce\x8c\x5d\x10\x5d\xf3\xcc\x09\x80\xf2\x4b\xea\x53\xe2\x4b\x16\x94\xd4\x9e\x18\x38\xde\xe3\x4c\xe6\x39\x45\xa7\xf2\xc2\xe4\xf7\xa5\xea\xcd\xdf\x1b\x59\xc9\x32\xc9\xcc\x4d\xf7\xfa\x3c\x1d\x9d\x1c\x50\x54\x1a\x20\x16\x4c\x54\x05\x25\xfb\x38\x03\x19\xcd\x89\x13\xc9\x68\x4d\x62\xca\x80\xe1\xfc\x48\x69\x9b\xd0\x35\x17\xf9\x72\x53\x22\x1b\x30\x83\x68\x97\x36\xd4\x24\xe9\x65\x29\x98\xe7\x3a\x4d\x91\x7f\xe9\x87\x63\x88\xd8\x45\xf9\x02\x30\x3b\x37\xd5\x9c\x48\xce\x2a\x61\x74\x19\x7f\xc6\xa9\x9e\x64\xb8\xc2\x92\x03\x7b\xe8\x6a\x14\x0e\x46\x56\x56\xdd\xbf\x29\x60\xd5\x66\xab\x7c\xa1\xf4\xe3\x78\x8c\xfd\xe8\x16\x05\xf1\xce\x78\x96\x8e\xd0\xaf\x23\x9c\xd1\x78\x26\x3c\x17\x2d\xb8\x6b\x4d\xfd\x04\x18\x05\x7b\x95\x73\x6d\xc1\xae\xef\x10\x0e\x44\x70\x7a\x18\xf1\xfb\x6f\xf3\x02\xe0\x16\x25\x24\xd7\x9a\xe1\xa9\x72\x5d\x71\x39\x16\x04\x63\xcf\x14\xac\xc6\x5a\xa5\x45\x95\xc5\x47\x07\x7c\x41\x9d\x09\x5b\x22\x39\x71\x5b\xb4\x25\xe4\x35\x37\x4e\x83\x91\x75\xa9\x55\xc8\x47\xc9\xd0\xcc\x45\xf7\xbc\x78\x2e\x2b\x6c\x68\x29\x99\x8b\x0a\x73\xe8\x79\x6d\x7b\x44\xbf\x5e\x3c\x8b\x32\x4e\x5f\x16\x66\x42\x80\x46\x34\x91\xf0\x11\xc4\x2d\xde\x50\xf1\x5f\xd5\x9a\x7c\x6d\xf2\x22\xd7\x90\x33\x0c\x8e\xe2\x59\x14\xa0\xd9\x94\x3a\x14\x0e\xc6\xb3\x00\x6b\x74\x6f\x56\xd3\x30\xca\x8d\x5c\xe4\x0f\xe5\x63\xdb\x0a\x2c\x82\xf8\x2a\x92\xf1\x88\xa3\xf1\x0d\x1a\xce\xc4\xa2\xb4\x44\xd2\x5f\x5d\x45\x63\x9c\x52\xa7\x4a\xbb\xac\x05\x7c\x23\xc1\x13\x3f\x8c\x54\xe1\xaa\x5c\xbf\x26\xfe\x75\x45\xe9\x17\x5c\x9c\xa2\x65\x5b\x66\x76\x6f\xfe\x95\xaa\x98\x73\xaa\x79\x70\x4d\x39\x50\x32\xc7\x43\x69\xfd\x25\x92\x08\xd0\x45\x4f\x40\x1b\x4e\x72\x22\x5f\xd5\x3e\x86\x51\x45\x6e\xf2\x25\x6a\x79\x0a\x9d\xd9\xcc\x27\x79\x06\x6f\x1b\x91\x10\xba\x93\x00\x16\xbb\x6d\x51\x3e\x4f\xd5\x2c\xec\xf7\x1b\x79\x04\xc4\xdb\x25\x69\x3d\x39\x8d\x26\x08\x66\x38\x21\xa7\x49\xb1\x31\x2c\xe7\x07\x04\x70\x86\xb4\x57\x64\xdc\x45\xdd\x83\x04\x57\xb1\xe5\xaa\x77\xcd\x31\x52\x52\x60\xe5\x0c\x1f\xa6\xdc\x2c\xaa\x70\x5f\x99\x85\xe9\xc9\xb0\xe4\x11\xb5\xa0\xa1\x70\x32\xb4\xbc\x21\xcf\xf4\x7c\xaa\xe4\xb1\x45\x8b\xb0\x75\x2b\x9c\x54\xfc\x3d\xb9\xe9\xfb\x1a\xbb\x95\xce\x42\x59\xe8\xe4\x75\x4f\x2b\x37\xc7\x6e\xf8\x17\x99\xbc\x9d\x1b\x1b\x62\x8e\x89\x75\xc6\x0a\x2d\xde\x54\x1e\x26\x4e\x9a\x8e\x4c\xf4\xfc\x0c\x3e\xf2\x53\xc8\x90\xeb\x3c\x71\xcf\x4d\x45\x9e\xb3\x6b\xd9\x07\x8a\x4e\x3a\x83\x4e\xc3\xae\xe1\x14\xc5\x91\x74\x14\xae\x77\x50\xa5\x5d\x6f\x80\x25\x6b\xd5\x72\x2c\xde\xa5\x95\xf9\x31\x58\x3c\xda\xcf\xc3\x0f\x12\xf5\xb5\x28\x03\x59\x61\xc0\xd4\x22\x57\x33\x3a\x08\x0b\xe4\x24\xbf\x6b\x74\x3b\xd2\x10\xa2\x21\x92\xe7\x05\xb9\x2b\x6d\x43\x22\xe6\x40\x09\xdd\x76\xbc\xbb\xd9\x68\x77\xec\x4e\x62\x45\xa9\xae\xef\x1c\x61\x8d\xc7\x56\x2b\x1f\x66\xed\x18\x8b\xf0\x1e\x6e\x0d\x81\xa9\x86\x98\x63\x89\x9d\x6b\x52\xf8\xc2\x79\x78\x95\x09\xa3\x97\x87\x50\x91\x00\xc2\xb2\x8a\x47\x2d\xe1\x58\x49\x00\x5a\x61\x5e\xa6\xd4\xa0\xef\xcd\x6c\x38\x2c\x1b\x33\xdf\x90\x8f\x16\x1b\xeb\x4f\xd3\x00\x58\x86\x3c\xd8\x34\x2d\x7f\xf9\x8c\x7d\xce\x08\xc2\x14\xb8\x1e\x47\xb8\xb4\x0b\x11\x65\x45\xcc\x7f\x68\xee\xf2\x5e\x60\xce\x67\x80\x57\xe5\x05\x43\xca\xa6\x4b\x51\x4b\xce\x57\x9d\xd0\x82\x32\xa1\x28\x63\xe0\x58\x8f\x0e\x8d\x04\x53\xd8\xa8\x10\x2c\xe4\xc1\xc6\x97\x08\xe9\x04\x5f\x1b\x28\xe9\x1c\x6b\x8a\xbf\x0f\xe6\x3b\xb1\xc3\x92\xdc\xa4\x02\x17\x27\x83\x44\x1f\x63\x40\xd9\xcf\x68\xbe\x78\x56\x33\x8f\x19\x8a\xc2\x14\xe1\xe1\x10\x0f\xb2\xf0\x12\x8f\x6f\x90\x8f\x02\x9c\x66\xc9\x0c\x9e\x3d\x90\xd3\x97\xe3\x68\x80\x4b\x45\x19\x2d\x49\xa1\x4a\xa2\x07\x40\x29\x0f\xc8\x0d\x25\x16\xd7\x5c\x90\x41\x78\xa0\x9d\x01\x6d\x70\x72\x14\xc9\x84\x1c\x6a\x09\x47\xe9\x22\x42\x2f\xa8\x36\x9f\xea\x79\xd1\x85\xe8\x7e\xc7\x32\xbe\xe6\x81\xa8\x18\x0c\x9a\xb7\x56\xe6\x09\xf0\x0b\x70\x56\x69\x84\x38\x93\xdd\x91\xe6\xc1\xba\x78\x48\x79\xd7\xe2\x91\x92\xdf\xb5\xeb\x8d\xd5\x66\xa3\x9c\x98\x9f\x32\x8d\x8f\x12\xff\xde\x67\x93\xf6\x42\x04\x4e\x0a\xa3\x0c\x27\x43\xc9\x5a\x18\x39\x57\x05\xe7\xaf\xac\xeb\x9c\x6a\xe9\x76\xcb\xe2\x23\xfa\x68\x84\xc7\x53\x9c\x10\xf1\xa7\xc4\x22\xd8\x61\xb8\x31\xdf\x60\x1d\xe5\x6f\x70\x8f\x47\x65\x26\xdd\xa9\x82\x76\x75\xe5\x9c\xf6\x6a\x17\xba\x54\xb1\x09\x5b\x6e\xfd\x9c\x5c\x55\x31\x1e\x04\xd0\xae\xfb\x3d\x63\x5d\xd8\x03\xe0\x22\xf5\xbc\xc8\x56\x22\x1c\x16\xd5\x2c\x62\x79\x86\x4b\x95\xc2\x17\x3f\x36\x5a\xe9\x89\xb0\xe4\xdd\xfd\xcd\xde\xc3\xd3\x13\x11\xa1\x79\x50\x0a\xd2\x02\xa3\xab\xbf\x05\x4d\xed\x4e\xfc\x41\x29\xba\x9a\xf8\x83\xfb\xd0\x96\xa8\x7e\x2f\xfa\xfa\x8c\xed\x2a\x24\x89\xbe\x7a\xe7\x80\x16\x99\x07\x4a\x64\xb4\x11\x5a\x77\x31\x62\x2b\x3c\xfe\x0a\x4d\xd2\x1c\x1f\x06\x82\x0d\x38\x31\xb0\x1f\xb9\x17\x03\xcf\xd4\x02\x21\x7d\xf7\xfd\x6c\x44\xc3\xfa\x3e\xe3\xef\xd9\x30\xbf\xce\x23\xfd\xde\x9e\x79\xed\xd6\xf7\x1a\xde\x97\x21\x53\xe1\xe1\x88\xab\x0f\x1e\xef\x97\x43\x5e\x34\xee\xaf\xc0\x50\x8e\xff\xeb\x0a\xfa\x2b\xbe\x43\xf0\x5f\x5b\x00\x5d\xf3\x8a\x82\x47\x8d\xcd\xa7\x4c\x22\x00\x29\x1a\xac\xf4\xbe\x20\x3c\x8d\x52\x5b\x72\x81\x71\x85\x91\xed\xb4\xca\x99\x68\xb1\xb2\xdc\x48\x4b\x3c\xde\xcd\x4c\x8b\x55\xff\xeb\xec\xb4\xca\x22\xf0\x50\x9c\xb2\x0f\xed\xd9\x4d\xb5\x28\x2e\xff\x00\x5b\x62\xa3\xfc\xc4\x9f\x0a\xe1\x70\xe2\x4f\x17\x8f\xbd\x60\x71\x11\x37\x41\xb8\xac\x32\xe9\x98\xdf\xd5\x60\x19\x2d\x6d\xa0\xa6\xdb\x66\xf9\x26\xc3\x75\x8b\xd1\x32\xfd\x73\x99\x2e\xd3\x3f\xa7\x01\x33\x07\xdc\xc8\x01\x57\x42\xb4\x84\xea\x55\x8b\x4d\x34\xff\x52\xc6\x32\x9a\x03\x6e\x6a\x80\x1b\x4e\xc0\x0d\x2b\x60\x3b\xe4\x2c\x09\xa7\x63\xb8\x7a\xa9\xd0\x61\x79\xf3\x06\xfc\x26\xbe\xd2\xe7\x06\x79\x5e\x27\x8f\x80\x82\x0d\x8a\x98\x8a\x3f\xe8\x54\x54\xfe\x40\x6f\x48\xeb\x3f\xfd\x84\x00\x9b\x3f\xd0\x4b\x54\x5b\x59\x6b\x4b\x33\x54\x7d\x8d\xfe\x28\x08\x77\x21\xcd\x3d\xb5\x05\x9f\xf8\x53\xb0\x99\xdd\xcc\x2a\x15\x8e\x30\x74\xba\x83\x5e\xa2\x4a\x13\x2d\xa3\x3f\xaa\xac\xa7\xcd\xa1\xd5\xdb\xc9\x88\xcf\x60\x2a\x2e\x82\x80\xa7\xfb\x36\xa9\x91\x7d\x20\x28\xa1\x0d\x24\xa1\xd3\x31\x9c\x49\x20\xb6\x5e\x5e\xdc\x6e\x1c\x3c\x0a\xc7\x18\x55\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\xbc\xb9\xa5\xa8\xb5\xe0\xe6\x20\xea\x09\x43\x79\xf1\x46\x18\xca\xcf\xe7\xfb\x46\x89\x04\x5f\xe2\x24\xc5\xfb\x52\xc1\xfc\x95\x2d\xae\xd9\x0f\xf9\x67\x27\x75\x17\x02\xb5\x6d\x01\xfc\x4f\xe7\x3f\x84\xfd\x90\x15\xca\x3a\x58\xc8\x69\xd4\x86\x4f\xf9\xc2\x66\xb6\xf9\x7f\x54\xcf\xd0\x06\xfa\xa3\x5c\xac\x4e\x0b\x4b\xd9\xbb\x88\xe2\x04\x7f\x33\xae\x22\x81\xdc\x8b\x02\xf0\x73\xce\xa7\x3b\x24\x6f\x0e\x86\xf3\x78\x86\xd4\x0e\x85\xf1\xc3\xc6\x06\x5a\xae\xcf\xe1\x49\x32\x85\xc9\xb5\xef\xc4\x88\xad\x22\x41\x22\xd2\x5e\xa6\xf8\x43\x1c\x4f\xf3\x25\xe1\xe9\x38\x78\xd2\x8c\x2a\x22\x87\x76\xe3\xe9\x4f\xbb\xe8\xc5\xe6\xdb\xde\xd6\xf6\xce\xbb\xdd\xbd\xff\x7a\xff\x61\xff\xe3\xc1\xe1\xff\x3e\x3a\x3e\xf9\xf4\xcb\xaf\xbf\xfd\xfb\xff\xf8\xfd\x41\x80\x87\x17\xa3\xf0\x8f\xcf\xe3\x49\x14\x4f\xff\x3b\x49\xb3\xd9\xe5\xd5\xf5\xcd\x97\x5a\xbd\xd1\x6c\xb5\x3b\x6b\xeb\xaf\x96\x56\x37\x58\x84\x5b\x71\xb4\x13\x8b\x76\x61\x54\xf3\x21\x76\x78\xa5\xe4\x96\x1b\x8a\x85\xa9\x4d\x14\xd2\xda\xb1\xb9\xa9\x90\x99\x0e\x1d\xfb\x0d\x73\xec\x4a\x89\x90\x24\x2d\x8f\x9c\x9a\x64\x07\x16\xb4\x8c\xea\xd5\x33\xf0\x5e\xc9\x05\xa6\x86\x49\x5c\x1c\x68\xa3\x0c\xd0\xea\x19\xdf\xe0\x65\x31\xcc\x02\x95\x0a\x44\x91\x12\xb9\xe7\x2b\x11\x66\x00\xfd\xaf\xb4\x45\xd9\xb7\x26\x2a\x0e\xde\x83\xd8\x10\x2f\x2d\x29\x1f\x04\xd9\x8a\x1f\x8c\x22\x8d\xd8\x92\xd6\xb0\x08\xb7\x79\xee\x1e\xfd\x90\x2f\xed\x11\xaf\x9d\x99\x7d\xda\x4f\x47\xff\xa7\xa3\xbf\x38\xfa\x7f\x3a\xd9\x59\xae\x77\xd0\xdb\xed\xd2\x0e\x5a\xf5\xce\xdb\x6d\xd9\x47\xab\xde\x51\x9f\xe0\xeb\xdd\x9d\xb6\x28\x32\x7f\xad\xe3\x56\x49\x1c\x1e\xd0\x79\xab\xde\x71\x7a\x6f\xd5\x3b\xff\x00\x8d\x40\xf9\xc3\x3a\x0c\xc6\x7d\xce\xea\x76\x7f\x7f\xb0\x8c\x8a\x03\x7c\x18\x87\x51\xe6\x72\x32\xae\x77\x1c\x4e\xc6\xd6\xc3\x74\x8e\xa9\xdb\xcb\x58\x34\x59\xd6\xd5\x58\x02\x7a\x8f\x13\x94\x4e\xc4\xf7\x72\x56\x03\xda\x5c\x74\x6d\x7c\xd7\xc7\x28\xba\xaa\x84\xcb\x1a\x5f\x7c\x0b\xf9\xac\x41\xa5\xc5\x7c\x8d\x79\x2d\x21\xdf\xf2\x17\x8f\xed\x69\xac\x36\x5c\xce\xd1\xb8\x0e\xb2\x8f\xc0\x50\x75\x33\x26\x22\x50\xbe\x58\x1a\x64\xb1\x68\x41\xd8\xdc\x14\xee\x92\x72\xb4\xd1\x79\x59\x3e\x14\x06\x23\xcb\x0f\x25\xf6\x30\x69\x9f\xfa\x70\xef\x7d\xea\xc3\x77\xb0\x4f\x95\xc1\xe1\xa1\xf7\x29\xeb\x72\xfa\xb0\xfd\xb4\x4d\x89\xbf\x07\xdb\xa6\xd2\x2b\x7f\xba\x1d\x05\xa1\x1f\x55\x16\xdd\xb1\x6c\x47\xf2\xef\x7f\xcb\xfa\xf0\x38\x5b\x56\x99\x65\xf2\xfd\x6f\x59\x1f\xb6\xb5\x4d\xeb\x69\xc7\x32\x76\x2c\x69\xc5\x2c\xb4\x79\x7d\xd3\xdd\x4b\xcc\x8b\x84\x2d\x01\xa4\xf4\x91\x47\xc3\x87\x2f\xec\xee\x84\x2e\xee\x5a\x8d\xfc\x3f\x5c\xac\xd0\x8f\xa4\xfb\xec\x2b\xfd\x96\x2f\xff\x79\xea\x02\x20\x2c\xb7\xb6\xa0\x73\x2f\x6d\x01\xcb\x51\xfb\x2d\x95\x06\x1e\x92\x5e\xa5\x23\xbf\xae\xbd\x1a\x4d\xfc\xc1\x23\xaa\x16\x3c\xc4\x9b\x85\x5f\xd0\xda\x3f\x41\xdd\x60\xe4\x8b\xbd\x83\x2a\x42\x31\x62\x91\xbe\xec\x6f\xb5\xa1\x26\x98\xdc\xec\x6f\xb5\x6d\x32\x1e\x98\x38\x7f\xc6\x37\x34\x0b\x36\xb5\x83\x15\x7d\x05\xe7\x5f\x3f\xca\x78\x12\xef\x38\x99\x50\x1b\xed\xed\x5f\x0e\xcf\x61\xd3\x3d\x89\xdf\xe3\x5c\x18\x44\x57\x57\x57\x2b\xf1\x14\x47\x69\x3a\x5e\x89\x93\x8b\xd5\x20\x1e\xa4\xab\x90\x84\x3b\x5e\xd5\xea\x8c\xb2\xc9\xd8\xa2\x08\xd9\xbe\x9c\xbe\xdf\xda\xc9\xd1\x16\xcf\x25\x83\x21\xcc\xf7\x01\xd1\xf6\x38\xc3\xfb\x85\xa5\x3c\x87\x3d\x8a\x0c\x4c\x4a\x1e\xc2\x88\xbb\xbd\x48\xe1\x9e\x73\x57\x97\x16\xaa\xd4\x1b\xeb\x8a\xa7\x8b\x01\xdf\x61\xa4\x26\x87\xc5\xd0\x13\xa4\xec\x6f\xb5\xe7\x61\x1b\x66\xcc\x16\x59\x0f\x52\x2d\x7d\xc8\x62\x34\xa5\x56\xa7\xb2\x77\x8e\x63\x87\x33\xfc\x62\xb4\xdd\x81\x0d\x4f\x17\xd5\x1b\xeb\x60\x42\xaa\x7c\xa5\x9d\x03\xcc\xb5\x2f\x39\x3e\x4a\xdb\xb7\x77\x76\xbb\x71\x10\xed\x63\xfb\xe1\x60\xa9\xd1\x07\x30\xb3\xfe\x1c\x0c\x0d\xef\x1b\x4a\xf3\x73\x52\x34\xcd\xaf\xf8\x67\x3e\x57\xeb\x5a\x3e\xbf\xbb\x82\xf1\xd4\x69\xac\xd5\x6a\x3a\xe0\x05\xbd\x83\xe6\xfa\xfd\x94\x93\x77\xb7\x20\x85\x3f\xa1\x11\x42\x15\x90\x08\xdb\x87\x0c\xac\x64\xd1\xde\xc5\x4a\x9f\xd7\xa5\xb1\x00\x6c\x80\x0a\x2a\xa7\xfe\x38\x43\x9b\xf0\x9f\xc5\xc5\x62\xa0\x2e\x4a\xde\x0f\x41\x5e\x98\x6c\x1e\x9f\x83\xe1\x0a\x75\x8b\xc0\x15\xde\x19\x0f\xf0\x2b\xc8\x5b\x03\xc5\x95\xfc\x8e\x6a\xcd\x85\x04\x5e\x75\x8a\x2d\xe2\x2d\x59\xe9\x8c\x7b\x98\xb5\x85\x97\x1a\x21\x0f\x66\xa2\x9c\xaf\x0e\x2b\x2c\x97\x5b\x18\x84\x16\xa0\x43\xfc\x1e\xc6\xc6\x96\x12\x6d\x91\x33\x72\x0e\x4c\xf8\x04\x8b\x37\xce\xe3\x32\xdf\x63\x68\x8f\xd8\x93\xa5\x9c\xc4\xc4\x69\xd1\xfc\x85\x05\xcb\x77\x6c\x63\x22\xe0\xd5\x8f\xcc\x98\x45\xc3\x95\x1b\xb4\xbc\xe1\xf8\x58\x8f\x02\x44\x8c\x03\xcf\x01\xe7\x05\xb3\xea\xb2\x44\xcb\xce\xbf\x56\x46\x72\x30\x86\xdc\x09\x84\x41\xe1\xc4\x26\x19\x05\x1b\xf4\xaa\x36\x2f\xfc\xe9\xcc\x12\x84\x26\xc4\xc0\x99\x9f\x95\x83\x52\x9d\x1e\x94\xa4\x81\x2e\x4c\xfb\xa3\x61\x2f\x90\x75\x8e\x82\x0d\x63\xcb\x50\x99\xef\x24\xb2\x62\x31\x63\xac\x6d\x68\xa3\x2c\xd5\x92\x74\x34\x9c\xfe\x2c\xd1\x2e\x44\x80\x39\x5e\xaf\xac\xcd\x75\x29\x1e\x2c\xfb\x1d\xdf\x89\xf7\x2e\xc8\x77\x1f\xd0\xfb\xd6\xe2\x57\x26\xf5\xa6\x3c\x37\x97\x2a\x29\xda\x0d\xe9\xbd\xca\xdd\xf3\x0f\x48\xe1\xea\x62\xd3\xa6\xfb\xb5\x8b\xb3\x2f\x56\xcd\x43\x0e\xb1\xe1\x3e\x60\x0a\xc5\x06\xa1\x42\xce\x65\x7d\xd7\x9e\x63\xba\xb0\xb0\x61\x57\x25\x16\x70\x5c\x29\xde\xef\x6e\x5f\x17\x1c\xdf\x29\x34\xfb\xd9\xdd\xe3\x87\xcf\x6e\x7b\xdd\xe3\x47\xd2\xee\xda\x1a\x39\xd3\xaf\xfd\xad\xcf\xf4\x83\x70\x3a\xc2\xc9\xf2\x23\x9b\x08\xc0\xe9\x5d\x6e\xea\xaf\x39\xc4\x9b\x99\x3b\x1f\xe4\x34\xdf\x83\x8e\x1d\x12\x8e\x93\x8a\x43\xbb\xfc\xd2\x6d\x42\x20\xde\x6b\x99\x30\x94\x1a\xe4\x0c\xe7\x67\x50\x89\xfe\xe4\x8c\x98\x55\xdc\x81\x97\x19\x8b\xaa\x40\x8b\x2c\x90\x4e\x83\x9c\x6e\xe8\xdc\x64\xf8\x3a\x23\xa7\x48\x9f\x3d\xa3\x29\xed\x13\xf3\xcd\xe2\xa9\x36\xfc\x00\x0f\xc2\x89\x3f\x1e\xdf\xb0\x34\xa0\x41\xe9\x9b\x1b\x79\x54\x6e\x59\x2b\x6c\xe0\x4e\x04\x1a\x6a\xb3\x8b\x27\xe3\xb8\x0b\x7e\x8f\x9a\x9e\x23\x9f\x12\xe9\x56\x47\xee\xfc\x62\x17\x3b\x4a\x4d\x87\xa3\x96\x5c\x66\x25\x9f\xdd\x3c\x81\xc4\x2e\xbe\xbe\x63\x26\x08\xcb\xf0\x4a\xe4\x23\xdf\x37\x2c\x38\x9d\xda\xcd\x43\x18\x4d\x67\xd9\x7d\xe6\x94\x93\x87\x4a\x74\x77\xa0\xb3\x87\x22\x8e\x81\xc6\x28\x2c\xf4\x71\xe7\xa4\x12\x30\x5a\xf6\x10\x36\xf9\xe4\x6c\xa0\xbc\x0d\x5a\xe1\xb5\x95\x7a\x7a\x0a\xf5\x70\x8d\x40\x0e\xa8\x2b\x03\xbd\xb5\xeb\xe6\xdd\x3b\x6d\xde\x5d\x6d\xb7\x95\x36\x88\x6e\xbb\xe1\x69\xca\xf3\xf5\x27\x53\xbb\x7f\xba\xee\xdb\xb5\x3b\x1a\x91\xcc\x8b\x34\xe1\xe6\x21\x05\x1c\x80\x85\xc6\xd5\x9a\x88\x8a\x94\xd8\x90\x1d\x55\x1f\x26\x21\x3d\xb8\xbc\xce\xe5\x78\xa5\x95\xc4\x25\x55\x51\x44\x56\x07\xe7\x65\x3c\x48\x70\xf6\x40\x4a\x25\x22\xff\xee\xda\x03\x07\x41\x2f\x19\x9b\xb0\x79\x22\x53\x47\xdf\xb2\x1a\x43\xd9\x39\xd8\x11\x20\xd8\xaa\x33\x12\xfa\x22\xea\xa3\x20\x1e\x75\x0f\xf7\x02\x6f\xb7\x87\x8c\x2f\x0b\x07\xa6\x39\xe1\x65\xe9\xa1\x4a\x8a\x2e\xab\x8f\x93\xdd\x10\xbf\x40\x31\x45\x3b\xfa\x56\x8a\x8b\xc9\xba\x5e\x14\x19\x53\xab\xc4\xf5\x05\x3a\x2c\x7b\x94\xcc\xcd\xf1\x38\xbe\x42\x7e\xd2\x0f\xb3\xc4\x4f\x6e\x10\x53\x2f\x7d\xc6\x37\x96\xb8\x83\x9f\x65\x8d\xc4\xcf\xd6\x86\x0b\x06\x4a\x57\xb7\x94\x1b\xad\x39\xce\x90\x04\xa5\x02\x37\x48\x88\xff\x06\xba\x8d\x38\x41\x61\x14\xe1\x04\xa2\xcf\xc6\xb3\x0c\x04\x08\x3d\x0a\x1f\xc4\x4c\xa4\x3a\x46\x4a\x86\xec\x81\xb6\x62\x04\xa4\xe3\x1a\x3f\xb9\x46\x68\xa9\xb1\x08\x09\xc4\x92\x56\x32\x2e\xd2\x47\x86\x52\xc1\x50\x2a\x68\x34\xf6\xdb\xc1\x11\xcc\x27\xbd\x06\x9c\xfa\x01\x1a\xc4\x51\x9a\xf9\x91\xde\xbc\x35\x89\x94\x3a\xc7\x6e\xc5\x9a\xc0\xfb\x34\x3c\x43\xbf\x6f\xa0\xda\x75\x7b\x40\xff\x67\x73\x87\x31\x0a\x37\x3b\xf4\x7f\xc5\x9a\xb1\x58\xd3\x89\x85\xda\xb3\x8d\x22\xff\x82\x38\x64\xb0\x03\x3d\x46\x14\x32\xc1\xc4\x1f\x24\x12\x59\x41\xbe\x32\x1b\x33\xb6\x0c\x24\x74\xda\xc6\xc7\x1d\x7a\x52\x55\x5f\x9c\x2f\x98\xbb\x45\x20\x83\x61\xfe\x6e\xe2\x8f\xed\x6f\xf6\x58\xf4\x31\xc0\x2b\x84\x25\x56\x18\x09\x65\xc1\x29\x2f\x13\x88\xcc\x28\xfd\xf0\xc1\xc8\x64\x92\xe0\xad\xcc\x0d\x3e\xf6\x58\xd1\xc3\x60\xa8\xff\xa7\x47\x0f\x9b\x23\xa6\x2e\x22\x22\x12\x1e\x9a\xd3\xd0\xdc\x08\x62\xee\x1a\x73\xa3\x88\xb9\xab\x3e\x52\x24\xb1\xfb\x73\xbb\x1e\x55\x4f\xc3\x78\x5b\xf6\x63\x22\x5d\xec\xda\x83\xa3\x15\x06\x1c\x2b\xe4\x98\xf2\x58\x69\x40\x73\x09\x85\x4b\x1a\xfc\x92\x49\xa0\x52\x75\x86\x1c\x9b\xf8\x03\xfb\x25\x91\x38\xf8\x3b\x8c\xe0\x5e\xfd\xad\x15\xe6\xd7\x9d\xd6\xb2\xe5\xf5\x38\xec\x2f\x13\x54\x02\xb0\x6d\x4d\xb5\xaf\x38\x1a\x2c\x83\x4d\xa3\xe5\x3d\x75\xb3\xd4\x3e\x4c\x82\xf6\x7c\xe3\xbb\x74\xe4\x37\xda\x3a\x48\xf2\xb2\xa1\x83\x4b\x47\x7e\xbb\xde\x30\x5f\x36\xd7\x2d\x25\x9b\xda\xab\x24\x9c\xe2\x49\x50\xef\xd4\xac\xb6\x7f\xca\xab\x69\xff\x73\x30\xd4\xdb\xc1\x97\xd3\xcf\xc1\xb0\xe8\xde\x41\xed\x7a\x1c\xe0\xe5\xc1\xb0\x6f\x7d\x9d\x25\x8e\xd7\xcb\x17\x63\x3f\x98\xf8\x91\xed\x73\x6c\x07\x86\x07\xfa\xeb\xa9\x1f\x2c\xfb\x51\x1a\x5e\xbf\x6a\xe8\x83\x40\x3e\x85\x69\x5c\xaf\xd5\x1b\xfa\x88\xb3\x4f\xaf\xd6\x5e\xad\xe9\x33\x44\x3e\x7d\xc1\x49\xcc\x5c\xaf\x2d\x5f\x23\xc7\x37\xaa\x23\x5b\x1e\xe1\x6b\xed\x83\x8f\x75\xe2\xa2\x71\x37\x02\xe3\x7d\x32\xd0\x27\x37\xf1\xfb\xfd\x30\xb3\xbe\x5c\x1e\xe3\x0b\x7f\x70\xf3\xd8\x77\x40\x62\xf5\xc0\x93\xbe\x68\xe0\x65\xbe\x56\xc4\x23\x5b\x22\xf0\x4c\x56\x86\x66\x16\xca\xd6\x81\xf8\xdd\x68\x89\xdf\x84\xea\xf9\x6f\x42\xec\xe2\x37\xfd\x95\x93\x76\x6e\x5f\x0a\xbf\x18\x21\x53\x0c\x28\xfd\x1a\x77\x58\x14\x1d\x4e\xad\xd2\x53\x96\xa8\x4f\x82\x36\xf3\xb7\xb1\x52\x83\x50\x22\x6d\x56\x26\x40\xf1\x46\xd0\x9d\xfc\x86\x92\x9b\x78\x23\x53\x99\x78\x19\xa9\xaf\x24\x9a\x82\x67\x42\x4a\xf0\x23\xa7\x20\x3a\x2a\x03\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\xb6\xeb\x5e\xb1\x36\xd9\x53\xe9\xaa\xdb\x6e\x79\x0a\xe1\x75\xdb\x6d\x2f\x9f\xf8\x6e\xbb\xe3\xa9\xa3\xd7\x6d\xaf\xe9\x37\xc2\x3a\x29\x77\x3b\x35\x8f\x51\x6b\xb7\x03\xf8\x08\x4a\xe9\x76\x1a\x9e\x4c\x2b\xdd\x4e\xcb\xb3\x51\x4b\xb7\xd3\xf4\x64\x0a\xe9\x76\xda\x9e\x4c\x3f\xdd\x0e\xe0\xa5\xd0\x4c\xb7\xb3\xe6\xe9\x54\xd3\xed\xac\x7b\x3a\xdd\x74\x3b\xaf\x3c\x83\x48\xba\x6b\x35\xcf\x42\x4e\xdd\x35\xc0\x9f\x2d\x89\xee\x1a\x60\xcf\x48\xa3\xbb\xd6\xf2\x0c\xe2\xe8\xae\x01\xe2\x84\x8c\xba\x6b\x80\x73\xbe\xce\xba\x6b\x1d\xf9\x02\xdd\xcb\x97\x6c\x77\x8d\x5f\xad\x93\xc5\xdc\x5d\x7b\xe5\xf1\xa5\xda\x5d\xaf\x79\xf9\x12\xee\xae\xd7\xbd\x7c\x71\x77\xd7\x01\x9d\x9c\x82\xbb\xeb\xd0\xb8\x60\x34\xdd\xf5\xd6\xed\x99\xd7\xa9\x3d\x5d\x1e\xfc\xf5\x97\x07\xbd\x11\x1e\x7c\x26\x9d\x82\x95\x42\xdd\x80\x68\x9a\xb3\x74\x36\x25\x03\x83\x59\x7c\x6a\xa9\xdf\x20\xc7\xd3\x90\xe6\xe8\x87\x0d\xf4\x82\x43\x7e\x61\xb1\x08\x11\x4e\x1a\x0f\x78\x5d\x51\x68\x8e\x2f\xda\x39\xc2\x43\x9c\x60\x38\xe8\x25\xe1\x05\x9c\xc9\xc2\x28\xcc\x72\x30\xe9\x6c\x8a\x13\x50\x5d\x6f\x68\xe9\x39\x24\x28\x9b\xb3\x8b\x09\x8e\x32\xad\x00\xca\x62\x34\xf2\xa3\x60\x8c\x95\x71\x93\x61\xf7\xad\x90\x15\x9b\x1a\xa8\x6a\xba\x03\x4a\xba\x6f\x1a\x4b\x9e\x9a\x40\x85\x51\xb6\x2e\x69\xe8\x87\x72\x7d\xa1\x98\x50\x67\xc7\x3c\xe6\xe7\x35\xa8\x12\xfe\x13\x81\x0a\x2f\x64\x6c\x94\x43\x84\x15\xb1\x98\xa6\xff\x02\x48\x97\x21\xbe\x72\xa1\xe8\x6c\x5e\x42\x78\x8f\xa3\x80\xbe\x7e\x55\xcb\x73\x82\x03\x2c\x41\x67\xcc\xab\xff\x40\xd6\x9c\xb0\x1d\x81\x45\x67\x07\x6e\x54\xad\x1a\xad\x38\xb1\xaa\x77\xec\x68\xb9\x5b\x5a\xac\xc6\x5e\x94\x35\x1b\x8b\x36\xb1\x58\x8d\x9d\x71\xec\xdf\xa5\x4a\xa7\x05\xef\xf3\xf2\x77\x24\xa5\x15\x4a\xc1\x1e\x92\x5f\xdd\x64\xf8\x00\x92\x03\x19\xaf\x6d\x79\x97\x15\xfa\xdb\xa5\x8b\x2e\x6f\xab\xcc\x8a\xc8\x4b\x2f\xa6\x42\xc8\xa1\xbd\x15\xb8\xa1\x0d\x3b\xce\x16\xcd\xc2\xf6\x35\xcb\xbe\x7a\x93\xd9\x8c\x9f\x17\x72\x17\xb4\xa1\xb2\x48\x3e\xed\xbc\xfe\x69\x78\x76\xa7\xe4\xd9\xb9\x39\x77\xf8\x05\x53\x55\x6d\xee\x38\xaa\x16\x15\x8c\x35\x4f\x6d\xe1\x21\xe6\x46\x68\xeb\x88\x32\xdf\xd6\xac\x67\x64\x34\xc9\x6b\x02\x0f\x45\x44\xea\x93\x99\xb9\xd9\xae\x3f\x9d\x8e\x6f\x58\xc3\x7e\x72\x31\x23\x2c\x3c\x2d\xf2\x57\x64\xfc\x7a\x65\x9a\xc4\x59\x4c\x70\x94\x39\x77\x91\xe1\x84\xbe\xfb\xd8\x15\x2c\x9d\xfa\x93\xac\xf3\xd7\xc8\x3a\x10\x30\xfa\x2f\x88\x4b\x64\xcd\xa9\x54\xc2\x44\x02\xb6\x58\x7a\x8f\x87\xb2\x5c\xb7\x4e\xaa\x9c\x30\x66\x21\x95\xa4\xaa\x4b\xed\xe6\xcf\x26\xe9\xb9\xf8\x4a\xa7\x65\xe7\x22\x27\x84\x4d\x6c\xd0\xe1\x5b\xf1\xfb\x29\xfd\x91\x86\x11\x0b\xc6\x4a\x58\x46\xed\xba\x5e\x63\x7f\x55\xf4\x55\x4d\xe3\xcb\x96\x57\xa5\x6a\xb5\x50\xdf\xdf\x6a\x6b\xd6\x14\x36\x03\x10\xdd\x6b\x12\x6d\xb0\x51\xb5\x18\x80\xf0\xb4\x37\x85\xb7\x63\xb9\x26\xd8\x9e\xab\xf8\xd4\xe4\xa4\xb5\xeb\xce\x5a\xab\xdd\x68\xd6\xea\x1e\xaa\x5d\xe3\xe1\x20\xf0\xfb\xeb\xaf\x2c\x79\x15\x6b\xd7\xaf\xd6\xfb\x7e\x30\x18\x62\x0f\x06\xa6\xd9\x68\xb7\xd6\x3a\x6a\xb9\x33\xe7\x8d\x98\x96\x46\x4f\xee\xc5\xbe\xc8\xa4\x67\xdb\xbb\xae\xfc\x29\xc2\xe0\x5e\x3d\x7f\x0f\xa9\x77\xdc\x3b\x86\xfb\xfa\x9a\xcf\x06\x45\xe2\x9c\xc0\xe3\xe9\x05\x51\xe8\x88\xc0\xbb\x7f\x2e\x95\xde\x3f\xe5\x0f\x67\x36\x97\x10\xe9\x33\x21\x38\xb3\x00\xf9\xab\x54\x2a\x12\x4c\xea\x29\x8e\xbe\x22\xf9\x25\xec\x75\xad\xaa\xe6\x23\x8e\xbe\x96\x04\xd8\x68\x55\x2d\x00\x21\x94\xb1\xe2\x92\x6e\x82\xbb\x9f\x71\xc8\xae\x72\x43\x61\xbf\xee\x57\x86\xb4\x86\xa4\x31\x45\x4b\xa8\xa6\x8b\x0f\x4a\xe9\xba\x56\xba\x5e\x58\xba\xa1\x95\x6e\x14\x96\x6e\x6a\xa5\x9b\x85\xa5\x5b\x5a\xe9\x56\x61\xe9\xb6\x56\xba\x5d\x58\xba\xa3\x95\xee\x14\x96\x5e\xd3\x4a\xaf\x15\x96\x5e\xd7\x4a\xaf\x17\x96\x7e\xa5\x95\x7e\x55\x3c\x3b\x35\x6d\x76\xe6\x4c\x66\x5d\x2b\x5e\x3c\x9b\xf5\x86\x56\xbc\x78\x3a\xeb\x4d\xad\x78\xf1\x7c\xd6\x5b\x5a\xf1\xe2\x09\xad\xb7\xb5\xe2\x6d\x83\x1b\xac\xae\x12\x86\xfc\x39\x8c\x2e\x48\xd5\xd0\x1f\xf7\x6d\x62\xb3\x4f\xb6\x81\x53\xeb\x40\xf5\xe1\x93\x75\x50\x06\xf0\xc9\x3a\x00\x01\x7c\x6a\xda\xd0\xe9\xe5\x77\xd0\xea\x37\x82\xc4\xce\x4e\xc5\xf7\x50\xdf\x43\x03\x0f\x05\x9e\xb4\x40\x3d\x84\xd6\x3c\xb2\x85\xd6\xce\x74\xde\x10\xd0\x7a\x81\x87\x44\xd5\x7c\x84\x3c\x84\xea\x0d\x0f\x9d\x9c\xd6\x8d\x7a\x03\x5a\x8f\xb6\x44\xab\xe6\x8b\x96\xd4\x5b\x23\xf5\x1a\x46\xbd\x3e\xad\x27\x90\xf4\xa5\x7a\x4d\x0f\xa1\x06\xb4\xd7\x34\xea\x15\xf5\xaf\x25\xfa\xd7\x5a\xa8\x7f\x6d\xd1\xbf\xf6\x42\xfd\xeb\x88\xfe\x75\x16\xea\xdf\x9a\xe8\xdf\xda\x42\xfd\x5b\x17\xfd\x5b\x5f\xa8\x7f\xaf\x44\xff\x5e\x2d\xd4\xbf\x7a\xcd\x63\xfd\xab\x9b\x04\x53\xd4\xc1\x7a\xdd\x63\x1d\xac\x9b\x14\x53\xd4\x43\x82\x25\xed\x61\xdd\x24\x99\x42\x12\x6d\x7a\x9c\x44\x4d\x9a\x29\xec\x63\x4b\xf4\xd1\x24\x9a\xc2\x3e\xb6\x45\x1f\x81\x6a\xcc\x4e\xbe\x7b\xe7\xe8\xa4\x87\x50\x9b\x76\xd2\xa4\x9b\x80\x56\xb4\x76\x92\xd0\xdb\x2b\x5a\xd1\x24\x9c\x01\xad\x68\xef\x64\xdd\x43\xa4\xa3\x27\xa7\x75\x93\x72\xfa\xb4\xa2\xb5\x93\x84\x63\x34\x6a\x50\xd1\x24\x9d\xa2\x3e\xb6\x45\x1f\x1b\x76\x5e\xe3\xea\x23\xa1\x39\xda\xc7\x86\x9d\xd9\x38\xfb\xd8\xe6\x7d\x6c\xd8\xb9\x8d\xab\x8f\x2d\xd1\xc7\x86\x9d\xdd\xb8\xfa\xf8\x2a\xef\xa3\x9d\xdf\x38\xfb\xd8\x12\x7d\xb4\x33\x1c\x57\x1f\x09\x63\x64\x7d\xb4\x73\x1c\x57\x1f\xd7\xf3\x3e\xda\x59\x8e\x93\x56\x9b\x1e\xef\xa3\x9d\xe7\xb8\xfa\xd8\x10\xb4\xda\xb0\x33\x1d\x57\x1f\xd7\x44\x1f\x9b\x76\xa6\xe3\xea\x23\x59\xfe\xb4\x8f\xcd\xba\x7d\x41\xee\xee\xba\x89\xb5\x05\xb8\x36\xed\x5c\x67\x77\xd7\xde\x49\x32\xac\x64\x6d\x9d\x9c\x36\xed\x5c\x67\x77\xb7\x60\x41\x76\xa0\xa2\x9d\xeb\xec\xee\x3a\x3a\xd9\xf2\x50\xa3\x09\x15\x4d\xd2\x29\xea\x63\x3d\xef\xa3\x9d\xe9\xb8\xfa\xd8\xca\xfb\x68\x67\x3a\xae\x3e\xc2\x44\xd2\x3e\xda\x99\x8e\xb3\x8f\x35\xd1\x47\x3b\xd3\x71\xf6\xb1\xe9\xb1\x3e\xb6\xec\x4c\xc7\xd5\xc7\x9a\xe8\x63\xcb\xce\x74\x5c\x7d\x6c\x8a\x3e\xb6\xec\x4c\xc7\xd5\x47\xc2\xca\x69\x1f\x5b\x76\xa6\xe3\xea\xe3\x2b\x31\x8f\x2d\x3b\xd3\x71\xf5\x91\x2c\x0f\xd6\x47\x3b\xd3\x71\xd2\x6a\x9b\xd3\x6a\xcb\xce\x74\x5c\x7d\x6c\xe4\x7d\x5c\xb3\x2f\xc8\xbd\x3d\xb7\xa0\xda\xa1\x9d\xb4\x73\x9d\xbd\x3d\x7b\x27\x81\xe6\x80\x07\xb4\xec\x5c\x67\x6f\xaf\x40\x0c\x68\x83\x08\x68\xe7\x3a\x7b\x7b\xf6\x4e\x12\xde\xd1\x80\x61\x6d\xdb\x45\x1d\x57\x1f\xc9\x7c\xd0\x3e\xb6\xed\x4c\xc7\xd5\xc7\xa6\xe8\x63\xdb\xce\x74\x9c\x7d\xac\x89\x3e\xda\x99\x8e\xab\x8f\xf5\xbc\x8f\x76\xa6\xe3\xea\xe3\xba\x98\xc7\xb6\x9d\xe9\xb8\xfa\x08\x34\x47\xfb\x68\x67\x3a\xae\x3e\x82\x48\x4e\xfb\x68\x67\x3a\xce\x3e\x36\x3d\xde\x47\x3b\xd3\x71\xf5\xb1\x25\xfa\xd8\xb1\x33\x1d\x67\x1f\xeb\xbc\x8f\x1d\x3b\xd3\x71\xf5\xb1\x21\xfa\xd8\xb1\x33\x1d\x57\x1f\x5f\x89\x79\xec\x34\xcd\x05\x09\xd7\x28\x19\x4e\x26\x38\x08\xfd\x8c\x39\x95\x81\xbb\x82\x5a\x8e\x1c\x71\xd1\x06\xaa\xc0\x7f\x97\x90\xaf\x6b\x58\x69\x99\x3a\x2b\x53\x27\x65\xfa\xf6\x32\x0d\x56\xa6\x41\xca\x0c\xec\x65\x9a\xac\x4c\x93\x94\x09\x0c\x6d\xae\xa6\xaa\xdc\xb1\x58\xea\x2e\x18\xd0\x16\x32\xa5\x8b\x6c\xba\x7e\xe6\xdb\x0e\xe6\x7e\xe6\x8b\x50\x3e\x7e\xe6\xbb\x95\x63\xd1\xdb\x30\x4b\x4f\xe2\xcc\x1f\x0b\x98\xd1\x96\x9f\xf9\xd4\x83\xe4\x25\x5a\xb7\x40\x87\x3a\x1f\xf0\x30\xe3\xd0\x85\xc7\x09\x94\x37\x3a\xe3\x4c\x79\x25\xd0\x3c\xcd\x41\xfe\xfc\xf3\xcf\xa8\x0d\x17\x6f\xb5\xeb\xf5\x5a\x7e\xdf\x96\x97\xf8\x17\x6a\x36\x0c\xe2\x50\xfb\xb2\x8b\x36\x10\xa8\xdd\x87\xe3\x38\x4e\x2a\x52\x27\x57\x15\xdd\xbb\xab\x73\x50\xf6\x03\xda\x90\x9e\xf4\x85\x23\x50\xaf\x54\x2a\x39\x6e\x4b\xa8\xd3\xa2\xf9\xd2\x5e\x41\x30\xd1\x56\x95\x2a\x6c\xec\xfa\x59\x5e\x95\xe1\x9c\x2b\x67\xe5\xb7\xe5\xb5\xb3\x26\x38\xa6\x9a\xd5\xc1\xcd\xd3\xcd\x1a\x5c\x62\x91\xce\xb6\xca\x74\xf6\x83\xb5\xb3\x1f\xee\xda\xd9\x0f\xd6\xce\x7e\x28\xdb\x59\xb3\xb7\xb2\x13\x55\x45\x74\x9f\x07\x9b\x82\x9c\x7a\x76\xff\x41\x30\x78\xa7\x6e\x0c\xe0\xa3\x68\xf3\xa4\x2a\xcc\x2b\x3f\xc7\x1b\x52\xd1\x79\x5b\xc8\x77\x97\x19\xc6\x3b\xbd\xdf\x16\xba\xf7\x70\x5c\x71\xa1\xa2\xeb\x7f\x81\x09\x5c\x61\xec\x9e\xda\xef\x2e\x76\xd9\x2d\x59\xa5\xb2\xab\x5c\x4b\xec\x2e\x7c\x1f\x41\x69\x61\x57\xb9\x8b\xd8\x75\x5e\x42\xcc\xbf\x71\x38\x62\xb9\x81\x61\x0e\x59\x04\x9e\x00\xc6\x54\x2d\x5a\x22\x59\x39\xb8\x21\x14\xb2\x7a\x50\xb0\x82\x53\xa6\xb8\xa1\x83\xc7\xfc\xfa\xdf\xd8\x78\xe1\xf3\xb9\x41\x0b\x2e\xef\x4a\x1e\x41\x83\x7c\xb5\x7b\x38\xd0\x5f\x02\x49\x4d\xf5\x75\xed\xa1\xd4\x43\xea\x15\x1a\xf0\x49\xb4\x81\x7c\xb4\x84\x2a\x95\x3e\xfa\x89\x6e\x8e\x95\xff\x4b\x7e\x06\x55\xc2\x06\xae\xd1\x12\xca\xa4\xf6\x44\xc0\xe2\x88\x4c\x53\x4a\x57\x2a\x8d\x53\xde\x6c\xa0\x65\x94\x56\xa1\x5a\x5f\x33\x7a\x13\x58\x69\xe7\xff\x72\x58\xc1\x76\x5c\x19\xa0\x9f\xd0\xff\x7d\x1c\xac\xb4\x43\xd0\x5c\xac\xfa\xe8\x77\x34\x40\xbf\x13\xc4\x1e\x1e\x19\x4d\x00\x9c\x8b\x0c\x41\xa4\xd2\x47\x5f\x1f\x78\x70\xe4\xdb\xea\x63\x57\x9a\xf4\xb9\x89\xf7\xcb\x04\x59\xe3\x7e\x62\x9a\x8b\x22\xac\x06\x13\x8c\xc3\x59\xcc\x51\xfa\xae\x61\xcd\xd8\xba\x14\x46\x2e\xfb\x5b\x6d\x8b\xef\x57\x71\x79\xd3\xe1\x2b\x8f\x2f\xa6\x5c\xe6\xab\x19\xf9\xf7\xb7\xda\x56\x93\x01\xe7\x24\xcc\xc9\x55\xff\x50\x53\x70\xa7\xd0\x0e\xf3\x27\x4e\xf6\xf2\x7b\x88\x89\xa3\x4e\x65\x62\x22\x76\x27\xfe\x80\x4c\x86\x92\x19\xde\x9c\x0f\x56\xcc\x9c\x93\x3c\x9b\x3d\x9d\x97\xc2\x0c\xec\x2c\xb2\xb5\xc3\x02\xaa\xf1\xb7\x76\x31\xfb\xe7\xc7\x64\xa3\x8b\xed\x25\x8b\x33\x84\x76\x30\x0e\xfa\xfe\xe0\x33\x8b\xab\x39\x89\x03\x58\x52\x84\x66\xc4\x7c\xc3\xcb\xde\xce\x5b\x22\x02\x59\xc4\x03\x30\x73\x82\xaf\x8a\xb5\x1c\x58\xb8\xd0\x56\xf6\x09\x00\x66\xcc\x23\x56\x7d\x6f\xe7\xed\xca\x76\x44\x63\x95\x83\x01\xd5\xce\x5b\x8b\xc1\xcf\xd4\x61\x2e\xc3\xcc\x0c\x0b\x4c\x66\xdc\xa2\x29\x0b\x41\xc5\x05\x12\xfa\x68\xbb\x67\x96\x42\x79\xd0\x42\x72\x28\x0f\xb5\x3c\x8f\x51\xfe\x1e\xdf\xa4\x59\x82\xfd\xc9\x66\x14\xb0\xde\x59\xac\x23\x63\x66\x16\x2b\xc0\x79\xac\x01\x9b\x90\x7d\x84\x27\x18\x82\x8c\x83\x31\x26\x9d\x27\x16\x2b\x13\xfc\xe7\x23\x7c\x9d\xd1\xd7\x76\xf1\x1d\x5f\xbe\x65\x31\x53\xa1\xf5\x95\x74\x1c\x0e\x70\x85\xa3\x20\x6e\xea\x05\x2e\x36\xfb\x49\x65\xd6\xb6\xf0\x3f\x65\xd6\xee\x31\xba\x60\x38\x3c\x0a\xd3\x85\xc7\xf6\x9b\xd1\xcd\x49\xde\xa1\x3e\x1e\xc4\x13\xe6\x75\x4f\x08\x22\x8c\x67\x69\x39\x92\x11\x5d\x2c\x25\x8e\x17\xf4\xa6\x32\xb7\x0b\x9a\x6f\x84\x79\x60\x83\xf3\xde\x65\x1e\xac\xe5\xf2\xb5\x6a\x34\x2e\x87\x63\xa6\xcd\xe7\x9f\x21\xb3\xeb\xa5\xf5\x48\x23\x4a\xa3\x0d\x14\x5e\xb2\x29\xac\x39\x56\x62\x7c\x89\xd1\xde\x2f\x70\xfe\x4c\x67\xfd\x14\xff\xf7\x0c\x47\x59\xc1\xe9\x19\xf0\x15\x0e\x0c\x73\x0d\xa0\x75\x7c\xb4\x09\x31\x27\x81\xfc\x31\x2a\xc7\x74\xa0\xa1\x60\x45\x00\xf1\x90\xda\x95\xd5\x55\xc4\x66\x24\x7f\x67\xcd\x96\x5b\x1c\x35\x86\x9a\x9e\xe7\x16\x82\x10\x09\x46\x34\x0a\xe7\x68\x83\x5e\x18\x16\x5c\x9c\xd8\x79\x5b\x64\x70\xcd\x37\x9d\x45\xe2\xd4\x75\x9a\x4f\xc2\xc7\xf7\x2e\x7c\xa0\xff\x9c\x26\x38\xc5\xc9\x25\xa6\x62\x48\x3c\x23\xa2\xbc\x24\x7e\x80\x1a\xc3\xcf\xc2\xfe\x98\x71\x60\xb4\x95\xa0\xb7\x49\xe8\x47\xe8\x1d\x75\xcf\x44\xc3\x70\x8c\x71\x34\x58\x19\x00\x08\x1e\xf2\x19\x22\x60\x6b\xf4\x73\x72\x04\x45\xfe\xcb\x8f\xd0\x6e\x32\xeb\xdf\xa0\x3f\x46\xe4\x3f\x2b\x57\xb8\xff\x9f\x17\x13\x3f\x1c\xaf\x0c\xe2\x89\x5d\xde\x39\x39\xe2\xcd\x15\x88\x3d\x72\xa1\xd2\xd2\xcf\xb3\x3c\xdf\x4b\x34\x20\x07\x05\x9a\x32\xe9\xf9\xb3\x67\x64\xd0\x81\xf4\x44\x3a\x24\x50\x12\x51\xa5\x50\x15\x66\x9d\xfe\xfa\x13\xad\xae\xc6\x97\x38\x19\x8e\xe3\x2b\x52\x07\x36\xbe\x3a\x4f\x07\x4a\xea\xd5\x3b\xd5\x9f\x48\xd9\xd7\xe2\x73\x43\xfe\xbc\xae\x7f\x6d\xb2\x3d\x8c\x35\x06\x78\x02\x2a\x04\xac\x68\x77\x75\x15\xf1\x66\x51\xbf\x4e\x8a\x00\xca\xd0\x74\xed\xb5\xa8\xd2\xc8\xab\x88\x32\xcf\x00\x01\x5a\x88\x96\x6a\xaa\xa5\x58\xb1\x67\x80\x0a\x2b\x77\x0b\xff\x12\x82\x94\x4b\x2c\x2d\xf5\x9b\xd2\x77\xf8\x87\x97\xa1\x45\x96\x96\xfa\x8d\xd7\xcf\xdd\x05\x96\x96\xfa\x75\xf6\x9d\xfc\x0b\x1d\xe7\x8d\xc2\xc3\xd2\x06\xf4\xfc\xcd\x1b\x96\x0f\x52\x7e\xdd\xa0\x2a\x40\xe5\x2d\x43\xc8\x6c\x49\x54\xab\x5d\xd7\xea\x4c\xeb\x97\x17\x65\x5c\x8f\x14\x22\x2f\x6f\x75\xea\x60\xcb\xa3\x32\xa0\xff\x55\x69\x84\xbd\xa4\x37\x48\x9c\x94\xf2\x97\x55\x46\x30\xd2\x14\xac\xae\x22\xb2\x4b\xc0\x4d\x0c\x0a\xa5\x85\x44\x17\x8f\xb1\xd2\x5e\xa4\x08\xe0\xa5\x28\x8e\xc6\x37\x74\x39\x6e\xfd\x7a\x70\xb4\x85\xfe\x40\x6f\xd0\x3a\xc0\xe4\x0d\xd6\x6d\x58\xd0\xbb\x38\xb5\xb3\xec\x1b\xef\x2f\x5f\x4b\xca\x59\x40\xac\xab\x15\xc7\xeb\xbf\x50\xe6\x5c\x54\xe4\x34\x8a\x6b\x32\x8c\xd9\x2a\xe3\x89\xa2\x59\x3e\x60\x06\xea\x45\x12\x0f\x72\x4b\x3d\x20\x34\xd8\x1b\x29\x96\x81\xd0\x1d\xe4\x20\x34\x5f\x16\xe2\xd2\x01\x21\x6c\x93\xe6\x29\x2b\x7a\xa6\x8b\x46\xec\xb3\x84\xab\xaa\x7a\x5e\x44\x28\x42\x0e\xc1\x08\xdd\x4d\x38\x42\x0b\x0a\x48\x48\x95\xe7\xcc\x43\x57\x4e\xf7\xf2\xd9\x4b\x2c\x8d\xd7\x9a\x64\x25\x8a\x4b\x02\x96\x53\xc4\x92\x0a\x2f\x20\x69\xb5\x9e\x24\xad\xef\x5d\xd2\x72\xc8\x57\x0e\xf5\xce\xc9\x51\xb1\x9c\xb3\xa8\x7a\xc7\xc2\xd2\x75\x5e\xfe\xc4\xc4\xff\x79\x4c\xbc\xf0\x34\xfb\x08\x2c\x7b\x2f\x1a\x24\x18\x22\x37\x30\xe0\x1a\x48\x26\x87\xe4\x93\xbb\x8c\xa8\x31\x8d\xe3\x0b\xdc\x96\x7f\x45\xb5\xbf\xd5\xe6\x50\x76\x57\x98\x7f\xde\x26\x65\x16\xd8\x05\xda\x4f\xbb\xc0\xdf\x62\x17\xd8\x1e\xe3\x41\x96\xc4\x51\x38\x40\xbd\x38\xc0\xfd\x38\x9e\xaf\xf0\xdf\xee\x15\x29\xfc\xe9\xd7\x85\x76\x84\xed\x9e\xaa\xf0\x27\xcf\x0f\xb5\x03\xc8\xac\x5d\x65\x20\x6a\xbd\x22\x2d\x26\xc1\x47\x59\x48\x8f\x85\x5f\x80\xef\x84\x1f\x4f\xbd\xd4\x9b\xaf\x37\x83\x32\x0b\xac\xe3\xbf\x77\x72\xe4\xff\x39\xeb\xf8\x60\x96\x4d\x67\x59\xf9\x4b\xbb\x83\xc2\x4b\xbb\x83\xc5\x2f\xed\x74\xa9\xee\x40\xbb\xc4\x3b\xf8\x6b\xaf\x83\x1e\x5d\xaa\x33\x75\xf3\xe2\xcd\xc3\x4a\x76\x05\x0d\x7d\x2f\xd2\xdd\x3f\xe9\x84\x7d\xa0\x5d\x6b\xba\x84\xa8\x83\x12\x97\x16\x07\x0b\x5e\x5a\x3c\x65\xb1\xfb\x7b\x30\xdf\xcd\x8f\xc7\x7b\xe8\xb7\x95\x57\x8d\x26\x37\x10\x47\x69\x46\x96\xf7\xc5\x8d\xc1\x7d\xa7\x7e\xb0\xb2\x19\xa5\xe1\x6f\xa4\xb4\xc8\x05\x37\xf5\x03\x99\xfd\x05\x7e\xe6\x4b\x17\xa1\xae\x0b\xd0\x54\xbd\x01\x25\xb5\x8e\x73\x83\x5f\xc5\x00\xf8\xb5\x5a\xb4\xaf\xa7\x15\xe9\xbb\x12\x8a\x00\x51\xcc\xa2\x4c\xf4\x4c\x0b\x66\x05\xb6\x78\x87\xf4\x9b\x01\x8c\xbe\x58\x56\x31\xfb\x97\xf6\xdd\x68\x8d\xc6\xb4\x19\xfb\x29\x8d\x9c\x85\xa6\x71\x1a\xaa\x1e\xf8\xa4\x51\xf2\x9d\xd4\x3f\x8c\x79\x67\x45\x0b\x4b\x1a\x46\xcb\xa8\xae\x35\x72\xe8\x07\xf9\x33\x0c\x94\xc8\x36\xa2\xbe\xa6\xac\x44\x6e\x2b\x0f\xa9\xa5\x36\x92\x87\xd4\x92\x4b\xdb\x82\x6b\xa9\x96\xd9\x4b\x1a\x20\x6e\x87\xc8\x2d\x70\x67\x91\x85\x38\x74\x8a\x78\x87\x33\x29\xe1\xbc\x32\x55\x54\x81\x2f\x46\xb3\x78\xe6\xa4\x3e\x57\x54\x34\x97\xc9\xf1\x97\xf5\x3d\xbf\x08\x92\x50\x60\xfb\x8a\xe1\x21\xa1\x81\x71\xf4\xf6\xf9\xb3\x5b\x2b\xdf\xe4\xcb\xe5\xfa\x55\xa3\xb9\x10\xef\xbc\x5f\x62\xb2\x27\xde\xf9\xad\x78\xe7\xde\xf1\x01\x82\x90\xb8\xe5\x58\xe7\x1e\x0b\xa0\x7b\x5f\xd6\xf9\x97\xb3\xc3\x7c\x49\xcc\xe1\x87\x16\x56\x45\xd3\x01\xd8\x23\xd0\xad\x24\x7e\x14\xc4\x93\x8a\xc1\x01\xab\xd5\x15\x4d\x52\x2a\x86\xc3\x52\x87\x9d\x1a\x5c\xae\xd1\x3a\xf3\x08\xb8\x27\x46\xa5\x33\x2a\x4e\x9c\x0b\x31\xaa\xbf\x77\xe6\x85\xff\x51\x8c\x6a\x75\x6f\xbb\x87\x5e\xad\xbd\x5a\x5b\xae\x23\x46\x1b\x68\x1f\x67\xa3\x38\x40\x0d\x17\xb7\x82\xd0\xde\x77\xe5\x56\x9b\x41\x40\xfd\x07\xd5\x05\x51\x82\x0b\xf0\xd5\x4b\x6a\xd3\x3f\xbe\x68\x95\x06\xfe\x0f\x4e\x62\xc8\x1d\x96\x8d\x30\x4a\x70\x2a\xf1\x45\xa5\x23\xa4\x1c\xeb\x31\x79\x36\xf0\xbe\x13\x2f\x60\x0b\xf1\x0b\xc3\x41\x5d\x8d\xce\xe6\x01\x34\x85\x67\x5f\xd8\x71\x84\xd1\x24\x4e\x30\x15\x1e\x97\x97\xa1\x6f\xae\x51\xe4\xeb\x7d\x79\xb9\xe4\x02\x87\xf9\x5c\x64\x81\xaf\xdd\x2f\xca\xf9\xd3\x02\xff\x66\xa7\x38\x14\xc5\xf1\xb4\x9c\x18\xf2\x91\x93\xa3\x73\x65\x0b\x62\x77\xaf\x89\xbc\x48\x11\xcd\x89\xa6\x16\x22\xba\xfb\x85\x9b\x7d\x22\xba\x6f\x45\x74\xff\x47\x62\x7e\xc5\x24\x27\xf1\xc0\xbf\x50\xf8\x2d\x7d\x70\x96\xcf\xb7\x86\x00\x5c\xa9\x14\x8b\xc0\x55\xf4\xf5\xab\xfe\xea\x4e\x5b\x8c\xbd\xc7\xf3\xe3\x0a\xac\xae\xa2\x4f\x04\xbe\x5a\x2f\x34\x22\x05\x80\x66\x41\x94\xb9\x1a\x85\x63\x8c\x2a\x3f\x54\x72\x5f\xeb\x3c\x06\x37\x78\x1c\x1a\x31\xb7\x85\x09\xa7\xa1\xc8\x0c\xc5\x96\x84\x54\x15\xa5\xee\xd8\x0d\xf1\x78\xcb\xec\x5e\x12\x05\x2d\xc4\x4b\xfe\xde\x8e\x5b\x96\x1c\x5d\x34\x49\xd6\xe3\xf2\x95\x3c\x13\x12\xb4\xf6\xd7\xe7\xf9\x78\xdc\x24\xe1\xe5\x62\x62\x1b\x31\xaf\xc5\x97\xe3\xdd\xcd\x7a\x1e\xeb\x99\x3c\x49\x1f\xcd\x44\xe0\x36\x07\xd1\x43\x3f\x4d\xc9\x42\x5e\x26\xa8\x05\xe8\x3d\xbe\x41\x5b\x38\x09\x2f\x69\x4e\xc8\x1d\x3e\x28\x8d\xe2\x98\xd3\x87\x6f\xdf\x6f\xed\x34\xf2\xd6\xc4\x73\xc9\xc4\xe3\xbd\x38\x1a\x86\x17\x33\x96\x89\x32\x86\xac\x90\x69\x51\x7e\xc9\x24\x9e\xe2\x24\xbb\x41\x7f\xd2\x63\x31\x78\x93\x02\xf3\x3d\x19\xd1\x1c\xc7\x29\x79\x08\x23\x96\x2e\x20\x8b\x85\x2f\xcd\x0a\xda\xc2\x43\x7f\x36\xce\xba\xa8\x85\x2a\xf5\xc6\x3a\x24\x52\xae\xba\xe0\x3b\x12\x9a\xe3\x84\x27\x32\xcf\xc1\x91\xf1\x9f\x87\x66\x98\xb1\xe4\x99\x29\x80\xca\x0f\xf5\xd2\x87\x2c\x46\x53\x9c\x0c\xe3\x64\x22\x01\x57\x20\x4b\xe9\x1f\x07\xc3\x8b\xae\x6b\x94\x11\xbd\xf8\x3a\x86\x98\x33\xf5\xc6\xfa\x6a\xb3\xa1\x85\xe0\xa6\x5d\xa1\xa8\x6b\x9f\x72\x84\x94\xc6\x6f\xab\x45\x09\x49\x8b\x12\xc8\x93\x59\x09\x72\xd2\xe2\xeb\x6d\x7e\x16\xd1\x03\xe0\x73\xb7\xa4\xab\x72\xc6\x50\x32\x7e\x03\x1b\xdd\x70\x7f\xb3\x61\x9c\xc0\x29\x26\x6f\xf4\x01\x12\x83\x7e\x0e\x86\x46\xd2\x78\x4a\xed\xfc\xf4\xa8\x98\x61\x2d\x52\xf1\xcf\x7c\xb2\xd6\x69\xfa\xc9\x7b\x83\xf1\xd4\x69\xac\xd5\x6a\x3a\xe0\x82\xec\xf5\x83\xe1\x85\xdd\xf0\x82\x4c\xc4\x86\xf8\xc9\x09\x8f\x14\x77\x05\xc3\x30\xd7\x3b\x5c\x57\x50\x0f\xba\xb2\x2c\xe8\x2e\xf9\x66\xa7\x0c\x36\x50\x0b\x7f\x58\x29\x59\x39\xf5\xc7\x19\xda\x84\xff\x2c\x9e\x88\x96\xbb\xd1\x48\x7e\xed\xf7\x21\x3b\x9a\x48\x3d\x18\xae\xb0\xa8\x24\x15\xde\x19\x0f\xf0\x73\x4e\x2a\x2b\x2e\xcf\xab\x56\x73\xa1\xdc\x2e\xea\xd4\x5b\x0d\x08\xc3\xcc\x91\x14\x96\x79\xd9\x83\xef\x3e\xa3\x55\x42\x3e\x94\x07\x79\x62\x76\xec\x66\x89\xee\x04\xe5\x20\x9b\xd2\xc1\xa6\xe9\xe6\x0d\x7d\x8e\x2d\xd4\x13\xc8\xc9\x7b\x51\x80\xaf\x6d\x35\x4e\x6b\xd7\x4c\x01\x64\x89\xd6\x39\x27\x44\x97\x40\x45\x08\xcb\xe2\x8d\x33\x7f\x7d\x8e\x0d\xaf\x94\xbf\x71\x56\xe2\x5b\xde\x06\x99\x95\x15\xf6\x64\x33\xc2\xc8\xb7\x16\x5a\x34\x7f\x31\xc7\xc8\x42\xfd\xc8\x04\x75\xad\x83\x3c\x2e\xd2\x1b\x8e\x8f\xd5\xb8\x40\x74\x92\xe5\x39\xe6\xc9\xb2\x81\x02\xf3\x34\xbe\x79\xaf\xf5\x39\x43\x2c\xa3\x77\x9e\x1a\xd8\xfc\x3e\x3f\x1b\x03\xc0\x57\x86\xd8\x3a\xba\x66\x71\x91\xc5\x28\x7f\xc5\x3a\xee\x40\x64\x4f\x8c\xb1\x1d\x74\x28\x47\xb3\x63\x60\x2d\x58\x28\xb6\x1c\x75\x6a\xcb\x21\x4d\x9f\xd3\x98\x03\x01\x3f\x57\x9a\x80\xd1\x13\x23\x2d\x7f\xb4\x8d\x75\x99\xf1\x46\xf3\x42\x41\xd9\x3a\xcb\x47\x5f\x7e\x67\x0f\x58\x25\x35\xf1\xdb\xc1\x91\xda\x1d\x70\x9d\xb2\x78\x5c\x1b\xe3\xf6\x07\xb5\x81\xf9\x83\xdb\xc0\x48\xb3\xf9\x1a\xfd\x51\x30\x7a\xe4\x2f\xaf\x71\xfa\x07\x98\xc3\x18\x1d\x39\xfd\x43\x37\x8b\xe1\x7f\xb7\xe6\x6b\x3d\xe0\x14\xf9\x93\x98\x03\xd3\x4d\x43\xa3\xb6\x29\xd1\x98\xc4\x69\xed\x6c\x69\xa9\xd8\xa4\x48\x02\x2e\x1d\x7d\x39\xdf\xb0\x04\x31\x63\x7b\x59\x5e\xaf\xc8\x80\x52\x3e\x46\xdc\x6b\x43\x2f\x13\x6c\xa6\x70\x23\x5f\x70\x13\x7f\x28\xd1\x32\x4c\x6d\xe9\xf6\xe7\x47\xaf\xb1\x88\x06\x0f\x10\xc4\x86\x8a\x08\x42\x32\xa4\x42\xa1\x4b\x4c\x58\xac\x9a\x87\x1c\xb2\xe9\x7d\xc0\x14\xca\xa6\x79\x90\x1d\x71\x94\x74\x09\x30\x1e\xd2\x05\x55\x36\xec\xaa\x58\x4c\x0a\xcd\x11\x9e\x6e\x8b\x6c\xd1\x28\x34\x7b\xa0\x1e\x3d\x85\x2e\xcf\x09\x7b\x7b\xe6\xad\xfd\xbd\x7d\xe8\x17\x48\xeb\x3e\x3f\x39\xfa\xe3\xea\x8e\x9c\xe9\xb5\x5d\x59\xaf\xff\x09\xda\xa5\x63\x30\xce\xec\x71\xe3\x5d\xaa\x44\x92\x5f\x16\xe9\x91\x04\x1e\x47\x78\x96\xfa\xfd\x31\x66\xe1\xc0\x24\x74\x8e\x91\x9c\x6a\x91\x42\xd1\xdf\xbc\x43\x6a\x86\x35\x69\x5b\x38\x82\x6c\xca\x88\x19\xda\x32\x1b\x63\x53\x93\x24\xca\x43\x8c\x95\x30\x45\x3e\xa2\x09\x98\xd1\x25\x4e\x52\x88\x5a\x36\xf2\x33\x14\xe1\x8b\x31\x1e\x64\x38\x20\x6c\x78\xc0\x52\xaa\x66\x4c\xe1\x93\xc5\x68\x1c\x66\xd9\x18\x2f\xd3\x00\x97\x2b\x2a\x50\x9c\x24\x71\x82\x82\x18\xa7\xd1\x8b\x0c\xf9\xc3\x21\x1e\xd0\xba\x14\xa9\x17\x29\x4a\xf1\x60\x96\x84\xd9\x8d\x27\x2a\xf6\x67\x19\x0a\x33\xa8\xc4\x6b\x84\x59\x2a\x02\x2a\x84\xe3\x30\x63\x4e\xdc\x34\xaf\x6b\x48\xf8\xf3\x04\x47\x74\x3f\x48\x6d\x8a\x32\x3a\x20\x1f\x68\xe7\x84\xba\x4c\x7b\x2b\xcf\xdf\x5d\x93\xb6\x15\x1f\x52\xde\xcb\x66\xd0\xce\x03\x46\x6e\xbd\x0d\xa7\x86\xcb\xa2\xd3\x42\xc8\x4e\x68\x64\xf7\xc2\xce\x73\xda\x6f\xa2\x5d\xf2\xcb\x92\x38\xee\xfd\x69\xed\xcc\x43\x95\xf7\xa7\xcd\x33\x16\x2c\x00\x7d\x25\x8f\xec\x2a\xa0\xde\xa9\x5a\x92\xc8\xbd\x3f\xad\xd3\x4a\x35\xb5\x52\xb3\xb8\x52\x83\x56\xaa\xab\x95\x6a\xc5\x95\x9a\xb4\x52\x43\xad\x54\x17\x95\xd4\x3a\xb6\xec\x48\xc6\x90\x71\x2f\x43\xd7\xa0\xf5\xc4\xa0\xf5\xec\x83\x66\xe2\x23\x0d\x17\xeb\x13\xbd\x30\x19\x0e\x79\xda\x41\x8a\x34\x0d\xb2\x5a\xab\x91\x2f\xb6\xfe\x9a\x13\xd1\x54\x21\xd7\xad\x90\x1b\xa5\x20\xd7\x9c\x03\x2f\xc1\xd0\x20\x37\x4b\x41\xae\xbb\x66\xc7\x93\x60\x68\x90\x6b\x1a\xe4\xf9\x13\xd9\xf3\x93\xe4\x06\xf5\xf5\x74\xaa\x74\xaa\xfa\x34\xfe\x85\xa9\xc9\xc8\xe8\xe4\x13\xd6\x93\xde\xa4\x19\x9e\xa0\x61\x3c\x4b\x50\x16\x4e\xf4\xb9\x5f\x30\x28\x6f\x84\xaf\xb3\x63\xb2\xfa\xdc\xf1\x63\x2d\x11\x6f\xf7\xe3\x20\x1c\xde\x50\x4e\x48\xe9\xb0\x04\x16\xeb\x6e\x2c\x7a\xa7\xd4\x71\xe0\xb7\x53\x48\x79\x09\xd1\x56\x8c\x4c\x71\xb6\x24\xb9\xbf\xa0\x14\x67\xb3\xa9\xfa\xa1\xc0\xa3\x63\xfe\x61\x7f\xef\x17\xea\xda\x51\x74\xc2\xdf\xfb\xe5\xbc\x86\x36\xd0\xde\x2f\x66\x6a\x34\xa9\x48\x9d\x16\xa9\x5b\xa3\x19\xcb\x4b\x1a\xa6\x32\x9d\xf5\x2f\x31\x11\x15\x5c\x47\xff\x1a\x0d\x7e\x0c\x6d\xd3\xe8\xc7\x5f\x11\x7d\x72\x45\x3f\x96\x8b\xb3\x30\xc7\xa2\x7c\x7e\x1d\x6a\x0f\x73\x2c\x9a\x6d\x88\x66\xeb\x4a\xb3\xf5\x79\xcd\xd6\xd5\x66\xeb\x8b\x35\x0b\x61\x74\xc2\x1a\x5f\x82\x04\x48\xd8\x50\x57\xa0\xab\x6a\x13\xaa\x36\xf8\x62\x86\xaa\x35\x75\x99\x3a\x66\x84\x91\x75\x11\x6b\x45\x40\xad\x35\x7a\xae\xd7\x63\xfb\xd3\x8f\x75\xfa\xb1\x6e\xfd\xd8\xa0\x1f\x1b\xd6\x8f\x4d\xfa\xb1\x69\xfd\xd8\x2a\x6a\xb3\x5d\xd4\x66\xa7\xa8\xcd\x35\xd1\x66\x81\x46\xaa\x14\xe7\x41\x8b\x73\x1f\x54\x8e\x03\x21\x53\x49\x21\xfb\x11\x3d\x48\x72\x57\xa7\xf2\x5a\x92\x3e\x4a\x71\x66\xb5\x88\xbd\x77\xee\xed\x1d\x06\x37\xf7\x32\x03\x2e\xa4\x96\x3e\xa6\xa1\x86\x7e\x03\x22\x44\x95\xdf\xc8\xdc\xf3\x55\x02\xcf\x62\xef\x7d\xad\x57\xac\xd3\x8a\x0d\x56\x71\x4d\xab\xd8\x76\x56\x6c\xd0\x8a\x2d\x56\xb1\xae\x55\x5c\x73\x56\x6c\xd2\x8a\x9d\x33\x81\x9a\x52\xb1\x9e\x57\xbc\xd7\x2e\x56\x14\xa5\x9e\x22\xc2\x63\xc7\x1f\xb3\x94\xec\x2c\x78\x3c\x3c\xde\x25\x7a\x3c\x87\xc3\x18\x9c\x80\x63\x8b\x1f\x6f\xc5\xd7\xea\x84\x87\xa4\x1c\xbd\xc2\x9b\xee\xb8\xd8\x8b\x4e\xa6\x7e\x61\xc7\x93\xdf\xdc\xe6\x1f\xc3\x4b\xfa\xa5\xd3\x5a\x6d\x36\x74\xb5\x9c\x58\x26\x82\x60\x2b\x25\x5d\xa1\x94\xf5\xa1\x7c\x91\x44\x50\xcd\xe0\xe7\xd8\xbf\xc4\x28\x1e\x07\x4e\x56\xbb\x80\xfc\xd0\x3b\xa7\x93\xdb\xd3\xe3\x1d\x2a\x2d\xf6\xfc\xf1\x60\x36\x26\x2b\x2c\xc2\x57\xce\x66\x7b\x2c\x11\x4c\x8f\x26\x82\xa9\x5d\xb7\x82\x26\xfc\x1f\x5a\xe2\x12\x9a\x9e\xaf\xa5\xc7\xf2\xc2\xf4\x68\x5e\x98\xda\x35\xab\xd1\x84\x98\xf2\x3d\x2e\xa0\xd6\xaa\xe8\x0d\xaa\xf4\xce\xa5\xe7\xff\x40\x75\xd4\x45\xb5\xaa\x09\xb1\xc1\x20\x36\x28\x44\x06\xb0\xc5\x20\xd6\x35\x88\xf5\x12\x10\x9b\x0c\x62\xd3\xe8\x56\x85\xb6\xa3\x40\x6c\x94\x80\xd8\x62\x10\x5b\xd6\x5e\x37\x35\x88\xcd\x12\x10\xdb\x0c\x62\xdb\xda\xeb\x96\x06\xb1\x55\x02\x62\x87\x41\xec\x58\x7b\xdd\xd6\x20\xb6\x4b\x40\x5c\x63\x10\xd7\xac\xbd\xee\x68\x10\x3b\x73\x21\xe6\x62\x3f\x05\xaa\x54\x5f\xd3\xab\xeb\xde\x31\x82\xa6\xc9\xee\x73\xb1\x7c\x8f\x45\x44\x4a\x5d\x5c\x03\xaf\x0e\x49\xd7\x7a\x96\x24\x1c\x3c\x5d\x7e\x32\x1b\x64\x68\x14\x5e\x8c\x90\x1f\x05\x68\x1c\x5f\x21\x3f\xb9\x98\x41\xf8\x17\x70\x73\xfe\xef\x99\x9f\x18\x89\x7b\xa0\x01\x1f\x6d\x90\x56\xb8\x14\x67\x51\x1e\x5c\xf4\x69\x11\xba\x4b\x58\x8f\x4f\xbc\xcf\x0a\x06\x09\x4e\x67\xe3\x0c\xc5\xc3\xa2\xe6\x47\x74\x0b\xa8\x5c\xf8\xe8\x25\xba\xf0\xa9\xeb\x4a\x7d\xad\x8a\x96\x10\x7d\xd5\x67\xaf\xda\xf0\xaa\x0f\xaf\x6c\x48\x8e\x29\x20\xa9\x2b\xf4\x48\xf8\x12\x5d\x5c\xc3\x0c\x57\x81\x20\x78\x01\x21\x76\x4a\x05\x6c\x89\x60\x48\x87\x7e\x3b\x38\x42\x10\x4e\x52\xfe\xf8\x8e\x72\xb8\x8b\x11\xfa\x1d\x5d\x8c\xcb\x32\x39\xbb\x52\xe5\x37\xc6\xe2\xde\x51\x16\x57\xa9\xbc\xcb\xb7\x6f\xb2\x93\xbd\x93\xc4\x82\x2a\x2b\xd0\x51\x0b\x74\xf2\x02\x3a\x3d\xff\xc6\xb8\xe1\x3b\xca\x0d\x2b\xb4\x99\x7c\xbf\x7d\xc7\xf9\x1f\xec\xb7\x4b\x88\xb4\x66\xc2\x68\x30\x18\x0d\x0e\xa3\xae\x22\x50\x37\x30\xac\xa9\x05\x6a\x45\x18\x36\x19\xf4\x26\x87\xde\x50\x31\x6c\x68\x18\xd6\x2d\x18\xb6\x18\x8c\x16\x87\xd1\x54\x11\x68\x1a\x18\x36\xd4\x02\x8d\x22\x0c\xdb\x0c\x7a\x9b\x43\x6f\xa9\x18\xb6\x34\x0c\x9b\x16\x0c\x3b\x0c\x46\x87\xc3\x68\xab\x08\xb4\x0d\x0c\x5b\x6a\x81\x56\x11\x86\x6b\x0c\xfa\xda\x99\x42\x22\x02\xc3\x8e\x86\x61\x5b\xc1\xb0\x54\xe2\x8f\x94\x27\x9d\x10\xba\xd6\x12\x69\x27\xe6\x5d\x77\x51\x58\x19\xbe\xce\xe4\x7b\x27\x59\x93\xca\x43\x29\x28\x69\x1c\xe8\x6d\x91\x79\x7f\x35\x1d\xfb\x04\x9b\xeb\x0c\x39\xc1\xb1\x38\x33\x95\xbc\x65\x1b\x44\x71\x71\x55\xa4\xd4\x55\x93\x77\xc8\x25\xab\x45\x77\x50\x72\xc1\xd2\xc6\xc8\x9e\x7a\x37\xd2\x6d\xb7\xbc\xfc\x52\xa4\xdb\xee\x78\xec\xae\xa4\xdb\xa9\xdf\x9e\x79\x6b\x7f\xef\x48\x84\x4f\xf7\x55\x4f\xf7\x55\x8f\x76\x5f\xa5\x2d\xf1\xfc\x3e\x47\xbf\xc9\xf9\x7b\xdd\xe1\x3c\x54\x56\xb8\xf7\xe2\x68\xfe\x5e\x3d\x9a\xbf\xbf\xeb\xd1\xfc\xbd\x7a\x34\x7f\x5f\x74\x34\x9f\xa7\x60\x7e\xba\xa9\x7a\xba\xa9\x7a\xba\xa9\x52\xbe\x3c\xdd\x54\x3d\xdd\x54\x3d\xdd\x54\xe5\xcd\x3e\xdd\x54\xe9\x1f\x9f\x6e\xaa\x1c\x8f\x4f\x37\x55\x4f\x37\x55\x4f\x37\x55\xf0\xf7\x74\x53\x55\x4e\x89\xfb\x74\x53\xf5\x74\x53\xf5\x74\x53\x25\xfd\x3d\xdd\x54\x3d\xdd\x54\x3d\xdd\x54\x3d\xdd\x54\xfd\x4f\xbe\xa9\x7a\xb0\x3b\xaa\xbb\xdd\x4e\x95\xb9\x97\x2a\x71\x23\xf5\x58\x77\x51\x7f\xef\x7c\x28\x4f\x77\x51\xff\xfc\xbb\x28\xf9\xee\xa8\xd7\x9a\xeb\xe8\x24\xdf\x1c\xf5\x5a\xd2\xb5\x11\x3c\x3c\xfe\x9d\x11\xf5\xd2\x14\xb7\x46\xf6\xa0\x02\xdc\x43\xbb\xe8\x5a\x09\xdc\x38\x65\x8f\x62\x29\x66\xba\xa9\xaf\x88\xc2\x0c\xa5\xfd\xf8\xda\x84\x73\x2c\xd0\x39\x96\xaf\xe9\xf8\x9f\x4d\x9a\x6c\xb4\x3b\xee\x43\x39\x3b\x74\x87\xf3\xd5\xb8\xef\xf1\x8d\x4d\x8f\xab\xb6\xe8\x71\xff\xf1\xb9\x0d\xb3\x41\x21\x43\xc0\xa3\x4a\x84\xe8\x5f\xf2\x38\x39\x54\x87\xac\x12\xd9\xda\xf8\xd8\x9f\x2a\x80\xcc\x48\x68\xca\x67\x23\x28\x9a\xed\xec\x4f\x7a\x51\xf9\x03\x2d\xd1\xf1\x59\xe2\x8d\x56\xd1\xbf\xa0\x57\x8e\x58\x0a\x57\xfe\xd4\x8e\x33\xec\x1b\xa6\x86\x40\x9a\x80\x63\xbb\x63\x3c\x79\x4d\x66\x7c\xfe\xf4\xf4\xac\x2a\x7e\x96\x55\x43\x10\xcd\x1f\x2c\xcb\xac\x00\x74\x6f\xb5\x1c\xd7\x84\x80\x16\xc4\xc8\xbf\x4e\xa6\xc7\xae\x32\x54\x5a\x16\x4e\xce\x8d\x76\xc7\xa1\x10\xa9\x39\x95\x21\xd6\x46\xcb\x2a\x46\xa4\xf5\xa4\x29\x46\xf2\x41\x0b\xb5\x2f\x7f\xe4\xc3\x39\x37\x03\x3c\x28\x07\xd5\xea\x9f\x65\x3c\xb5\xf9\x10\xab\x29\xa2\xcb\x28\xa2\x2a\xb5\xc8\xb2\x88\x42\xd0\xa0\xd3\x84\x71\x8c\x2a\x95\xef\x0a\x09\x3b\x08\xd7\x4a\xb4\x05\x04\xeb\x26\xd6\x9c\x50\xd5\xf7\x6a\x67\xbf\x92\xba\x15\xb6\xa6\x48\x15\x86\xd7\x59\x9e\xd7\x20\xd2\xf3\x18\x68\xc7\xa7\x4f\x10\x07\xc5\x72\xa3\x95\x93\x7a\x68\x9c\xdd\xc9\x58\x28\x73\xc5\xc4\x32\x05\xbb\xef\x55\xee\xed\xb5\x1e\x42\xe8\xed\xb5\x16\x96\x78\xcd\x3d\x56\x13\x77\x7b\x2d\x6b\x6c\x0b\xb8\xa1\x09\x71\x70\x87\x1d\x7e\x2b\x89\xa7\xca\x2e\xcf\x5e\xc0\x20\x7c\x83\xa8\x78\x01\x69\x4e\x0d\x34\xa7\xe9\xf9\xc9\xc4\x93\x52\x22\xd4\x1c\xaa\xbf\x6a\xc8\x60\xf5\x58\x73\x04\x75\x29\xea\x97\xb6\x8a\x09\xa8\xae\x0a\x42\x8d\x18\x57\x4a\x88\x21\x6d\xf0\x82\xc5\x77\x18\x64\x3c\x0b\x36\x70\x61\xf8\x42\xf0\x22\xbb\xf8\xcf\xb0\x99\x2f\x2f\x5b\xf7\xf0\x05\xd8\x3d\x9a\x93\x00\xe9\x3b\x5a\x6d\x64\x88\x1e\x66\xc5\x01\xa4\xc5\x57\x1d\xa3\xf9\xe2\x95\x47\x0a\x15\x9f\x34\x7b\xad\xc7\x3a\x66\xde\x2f\x5d\xdf\xb7\x3c\x5f\x3e\xda\x29\xf0\xdb\x06\x71\x26\xac\x0a\xa7\x38\xb9\xc4\xcf\x9f\x55\x06\x55\xd4\xa8\xd5\x1b\xa8\x7f\x83\x7a\xff\xdf\xff\x1b\x24\xe1\x00\xed\xe3\x34\x0a\xc7\x2b\x68\x73\x3c\x46\x49\x78\x31\xca\x52\xc4\xca\x07\x2b\xcf\x9f\x3f\x3b\xc2\x41\x98\x66\x49\xd8\x9f\x01\x7c\x3f\x0a\x20\x28\x4f\x18\xa1\x34\x9e\x25\x03\x0c\x6f\xfa\x61\xe4\x27\x37\x84\x1d\x4c\x52\x8f\x45\x69\x48\xe0\xbf\xf1\x2c\x43\x13\xe0\xe9\x03\xe0\xac\x1e\xf2\x13\x8c\xa6\x38\x99\x84\x59\x86\x03\x34\x4d\xe2\xcb\x30\xc0\x01\x0d\x3a\x41\xd6\xe9\x30\x1e\x8f\xe3\xab\x30\xba\x40\x83\x38\x0a\x42\xba\x86\x49\xa5\x09\xce\xba\x6c\xc5\x2f\x23\x15\xad\x14\x14\xc3\x14\x9f\x41\x1c\x60\x34\x99\xa5\x19\xd9\xa8\xfd\x30\x02\xa0\x7e\x3f\xbe\x24\x9f\xa6\x37\xd0\x45\x14\xc5\x59\x38\xc0\x1e\x8d\x2b\x34\x0e\x53\xd0\x2c\xcb\xed\x45\x81\x86\x4c\x10\xa6\x83\xb1\x1f\x4e\x70\xb2\xe2\xc2\x21\x8c\xe4\x81\xe0\x38\x4c\x93\x38\x98\x0d\xf0\x83\xa3\x81\x58\xd7\x82\x78\x30\x13\x71\x30\x48\x8d\xd5\x38\x61\x31\x32\x26\x7e\x86\x93\xd0\x1f\xa7\xf9\x30\xc3\xdc\x40\x35\x09\x75\x32\xcf\x27\xbb\x7b\xc7\xe8\xf8\x60\xe7\xe4\xd7\xcd\xa3\x6d\xb4\x77\x8c\x0e\x8f\x0e\x7e\xd9\xdb\xda\xde\x42\x6f\xff\x8d\x4e\x76\xb7\x51\xef\xe0\xf0\xdf\x47\x7b\xef\x76\x4f\xd0\xee\xc1\x87\xad\xed\xa3\x63\xb4\xf9\x71\x0b\xf5\x0e\x3e\x9e\x1c\xed\xbd\xfd\x74\x72\x70\x74\x8c\x7e\xdc\x3c\x46\x7b\xc7\x3f\xc2\x87\xcd\x8f\xff\x46\xdb\xbf\x1d\x1e\x6d\x1f\x1f\xa3\x83\x23\xb4\xb7\x7f\xf8\x61\x6f\x7b\x0b\xfd\xba\x79\x74\xb4\xf9\xf1\x64\x6f\xfb\xd8\x43\x7b\x1f\x7b\x1f\x3e\x6d\xed\x7d\x7c\xe7\xa1\xb7\x9f\x4e\xd0\xc7\x83\x13\xf4\x61\x6f\x7f\xef\x64\x7b\x0b\x9d\x1c\x78\xd0\xa8\x59\x0d\x1d\xec\xa0\xfd\xed\xa3\xde\xee\xe6\xc7\x93\xcd\xb7\x7b\x1f\xf6\x4e\xfe\x0d\xed\xed\xec\x9d\x7c\x24\x6d\xed\x1c\x1c\xa1\x4d\x74\xb8\x79\x74\xb2\xd7\xfb\xf4\x61\xf3\x08\x1d\x7e\x3a\x3a\x3c\x38\xde\x46\xa4\x5b\x5b\x7b\xc7\xbd\x0f\x9b\x7b\xfb\xdb\x5b\x2b\x68\xef\x23\xfa\x78\x80\xb6\x7f\xd9\xfe\x78\x82\x8e\x77\x37\x3f\x7c\xb0\xf6\x92\xe0\xae\xf4\xf1\xed\x36\xfa\xb0\xb7\xf9\xf6\xc3\x36\x6d\xe9\xe3\xbf\xd1\xd6\xde\xd1\x76\xef\x84\x74\x27\xff\xd5\xdb\xdb\xda\xfe\x78\xb2\xf9\xc1\x43\xc7\x87\xdb\xbd\x3d\xf2\x63\xfb\xb7\xed\xfd\xc3\x0f\x9b\x47\xff\xf6\x18\xcc\xe3\xed\xff\xfd\x69\xfb\xe3\xc9\xde\xe6\x07\xb4\xb5\xb9\xbf\xf9\x6e\xfb\x18\x55\xe6\x0c\xc9\xe1\xd1\x41\xef\xd3\xd1\xf6\x3e\xc1\xf9\x60\x07\x1d\x7f\x7a\x7b\x7c\xb2\x77\xf2\xe9\x64\x1b\xbd\x3b\x38\xd8\x82\x81\x3e\xde\x3e\xfa\x65\xaf\xb7\x7d\xfc\x1a\x7d\x38\x38\x86\xd1\xfa\x74\xbc\xed\xa1\xad\xcd\x93\x4d\x68\xf8\xf0\xe8\x60\x67\xef\xe4\xf8\x35\xf9\xfd\xf6\xd3\xf1\x1e\x0c\xda\xde\xc7\x93\xed\xa3\xa3\x4f\x87\x27\x7b\x07\x1f\xab\x68\xf7\xe0\xd7\xed\x5f\xb6\x8f\x50\x6f\xf3\xd3\xf1\xf6\x16\x8c\xee\xc1\x47\xe8\xea\xc9\xee\xf6\xc1\xd1\xbf\x09\x50\x32\x06\x30\xf8\x1e\xfa\x75\x77\xfb\x64\x77\xfb\x88\x0c\x28\x8c\xd4\x26\x19\x82\xe3\x93\xa3\xbd\xde\x89\x5c\xec\xe0\x08\x9d\x1c\x1c\x9d\x48\x7d\x44\x1f\xb7\xdf\x7d\xd8\x7b\xb7\xfd\xb1\xb7\x4d\xbe\x1e\x10\x28\xbf\xee\x1d\x6f\x57\xd1\xe6\xd1\xde\x31\x29\xb0\x47\x9b\xfd\x75\xf3\xdf\xe8\xe0\x13\x74\x99\xcc\xd1\xa7\xe3\x6d\xfa\x53\xa2\x58\x0f\x66\x12\xed\xed\xa0\xcd\xad\x5f\xf6\x08\xda\xac\xf0\xe1\xc1\xf1\xf1\x1e\xa3\x13\x18\xb2\xde\x2e\x1b\xee\x95\xe7\xcf\x5e\xae\xaa\x3a\xaf\x7d\x3f\x1b\x3d\xac\xde\xab\x5c\xd4\x69\x1a\xf8\x58\x14\xa1\x8f\xa5\xac\xb3\xe1\xc2\xce\x8f\xb2\x14\x65\x7e\x9f\x4b\x2c\xa4\xca\xf9\x97\xb1\x35\xd8\x66\x2e\x47\xd5\x3c\x84\xea\x1e\x42\x0d\x0f\xa1\xa6\x87\x50\xcb\x43\xa8\xed\x21\xd4\xf1\x10\x5a\xf3\x10\x5a\xf7\x10\x7a\xe5\xa1\x7a\xcd\x43\xf5\xba\x87\xea\x0d\x0f\xd5\x9b\x1e\xaa\xb7\x3c\x54\x6f\x4b\x16\x96\x6b\xb4\x2e\xf9\x46\xe0\x91\xf2\x04\x46\xbd\x4d\xe1\x92\x7a\xd0\xd6\x2b\x06\xbf\xc1\x60\xd4\xa1\x8d\x1c\x4e\x93\xb5\xd5\x62\xb8\xbc\x62\x30\xd6\x25\x3c\xd7\x18\xac\x0e\xc3\xa5\x4e\x61\xd6\xe5\x58\xcb\x75\x56\x97\xe3\x52\xa3\x30\x00\x0f\x8e\x67\x93\xc2\x22\xf0\xeb\x72\xbf\x65\x38\x2d\x56\xb7\xcd\x70\x5f\x63\x30\x1a\x12\x9e\x75\x06\x6b\x9d\xe1\xc2\xfa\x5d\x6f\x9e\x55\x5f\xcb\x73\x91\xcc\x99\x0b\x8e\xc7\x9a\x34\x56\x0d\x06\x93\xe3\xdc\x51\xc7\x03\xfa\xd6\xd4\xfa\xde\x61\x75\x9a\x39\x2c\xa8\xdb\xce\x71\xe6\x30\xf8\x78\x40\x5b\x75\xad\xef\x50\xa8\x2d\x75\x70\x8d\x21\xd8\xc9\x07\x57\x00\x69\x48\x03\x4d\x91\xcd\x01\xad\xb3\x3a\xd2\x60\xc1\xc4\xb4\xf3\xc1\x15\x30\x9a\xd2\x40\x53\x64\x25\x84\x1a\x6c\x64\x6b\x12\x30\x3e\x1a\x6b\x62\xf6\x04\x85\x22\x36\x3a\x14\x59\x75\x36\xd2\x79\x2b\x83\xa2\xc8\xc6\x0a\xd0\x93\x5b\xe2\xb4\xd5\x94\xc6\xb3\x93\x7f\x53\x68\x7a\xcd\x83\x4f\x30\x54\x9c\x5e\x5f\xe5\xb4\xc7\x69\xaa\xde\x96\x86\x75\x8d\x95\x55\xe6\xa3\x9e\x13\x81\x98\x8b\x57\xac\x20\x27\x9e\x75\xa9\x0c\x47\x7c\x0d\x7e\xcb\x67\x29\xb1\x96\x5b\x79\x55\xde\xbe\x58\xf3\xf2\x9a\x58\x57\x40\xe6\xa0\xf8\xfa\x6c\xe7\xb4\x2f\xfa\xd9\xc8\x51\x10\xe3\xc4\x48\x86\xc2\x45\xda\x94\xcc\x5b\x20\x0c\x31\x65\xf0\xdb\x39\x02\xd0\xcf\xb5\x7c\x21\x42\x83\x2d\x86\x48\x47\x43\xba\xa9\x0e\xbe\xe8\x74\x3d\x87\x23\xc6\x4e\x2c\x68\xf8\xae\xc0\x11\x0c\xa4\x2e\x0d\x52\x27\x6f\x57\x2c\x3c\xb6\x80\xeb\x4d\xcb\x7c\x88\x0e\x68\x88\x73\x40\x62\xc1\x35\xa4\xff\xb6\xc5\x2a\x56\x07\xa8\x6d\x29\xd7\x52\x67\x46\xcc\x64\xde\x29\x54\xaf\xa3\x33\x25\x4b\xf6\xf9\x88\xac\x10\xcb\x7c\x20\x11\xaa\xb9\xe6\xa1\xda\x75\x7b\x73\xbd\xb1\xf6\xea\xd5\x2b\xf2\xbb\xb3\xbd\xf5\x6a\xfb\xed\x66\x9d\xfc\x5e\xdf\xa9\xbf\x7d\xdb\xdb\xea\x91\xdf\x9b\xaf\xda\xcd\x9d\xad\xd6\xb6\x3a\xdf\xa3\xc4\xd9\x40\xbb\xb6\xd9\x58\x7f\xbb\xdd\x81\x06\x7a\xad\xad\xad\x7a\xa3\x05\x0d\x6c\xad\xd5\x9a\xdb\x3b\x4d\xf2\x7b\x6d\xb3\xb3\xb5\xd6\xd9\x86\x86\x39\x42\x67\x56\x7d\xc0\xd1\xde\xe1\xf6\xfe\x56\xbd\x53\x83\xf0\xfb\x73\x74\x48\xa2\x6c\xae\x45\x92\x5e\xd1\x5d\xf9\xae\x77\x45\x54\x99\x08\x48\x38\x82\x60\x77\xd6\x5a\xed\x46\xb3\x06\x23\xb8\xbd\xd3\xdb\xda\x7c\xbb\x0e\x1d\x7c\xb5\xfe\x76\x73\xab\xb7\xb3\x4d\x7e\xd7\x6b\xcd\x46\xbb\xb5\x06\x83\xd3\x6b\x6e\x35\xb6\xeb\x3b\xb5\x33\xa7\x6a\xbc\xac\x52\xde\xaa\xd8\x2d\xed\xa5\x54\x2f\xb8\xa9\x99\x6f\x8e\x4f\xb1\x00\xdd\x6b\x6e\x16\xe9\xb8\xbe\xd9\x3f\x97\x4a\xf3\xcb\x83\x73\xd3\x90\x09\x15\xdd\xa9\x48\xf5\xd0\x06\xaa\x98\x05\x10\x35\x00\x95\x1a\xcb\x0d\x1f\xa4\x97\x8b\x19\x95\x1a\x00\x99\x5d\xa9\x06\xd0\xb4\x2e\x35\xc1\x15\xa8\xc6\xd0\x3c\x5b\xe7\x5d\x24\xee\x1f\x08\x29\x3a\xaf\x1c\x81\x01\x9c\x8f\xc6\xee\x02\x09\x14\x48\x9c\x05\x40\xfc\x3c\xff\xe2\x86\x00\x32\xd1\xf9\x17\x37\x04\xd8\xa6\xcf\x53\x37\x04\xd8\x34\xce\xd3\xc4\x1e\xd1\x7a\x75\x95\xac\xb2\xcf\xe4\xd0\x7c\xe9\x27\x21\x91\x8e\x2d\x97\xb4\xfe\xd8\x43\xfd\xb1\x87\x06\x63\x0f\x05\x63\x0f\xe1\xb1\xa5\x21\x3f\xf1\x50\x3f\xf1\xd0\x20\xf1\x50\x90\x78\x08\x27\x7a\x63\x3e\x41\xc5\x27\x08\xef\x9a\x2e\x23\xfd\x04\x82\x8e\xc3\xc7\xba\xfe\x71\x40\x3e\x0e\xe8\xc7\x86\xfe\x31\x20\x1f\x03\xfa\xb1\xa9\x7f\x84\x03\x03\xa6\x1f\x5b\xfa\x47\x91\xa6\xda\x57\xf3\x52\xf3\x2e\xe9\xb7\x82\x56\x53\x42\xf8\xef\xd2\x06\xaa\x5b\xd7\x76\x46\x96\x8f\x3f\x46\x4b\xf9\x9a\x5a\xfa\x32\x3e\x0d\xcf\xce\xaa\x5f\x6d\x4e\x0c\xe0\xb5\xf3\xa6\xde\xa9\xfe\xf9\xfc\x99\xca\x1a\x49\x1b\x68\x58\xaf\xf4\xc7\xde\x60\xec\x05\xe3\x2a\x5a\x42\xa3\xb1\xdd\xf7\xe6\x16\x09\x85\x5c\xf8\xa6\xd9\xa0\xaa\x36\x0b\xb4\x86\x0e\xcd\x18\x79\x03\x5a\x6b\xdd\x09\xad\xa9\x43\x33\xa6\xca\x80\xd6\x69\x39\xa1\xb5\x74\x68\xc6\xdc\x4a\xd0\xfe\x5c\x5d\x65\x10\xd7\x6b\x4e\x88\x6d\x1d\xa2\x41\x10\xc8\x1e\x26\x9d\x4c\x62\x66\x9d\x2e\xf2\x05\x25\x71\x36\xae\x64\x5e\x4a\xa6\xd5\xe6\xb4\x01\x34\x90\x2d\xe1\xb1\x7d\xca\x61\x45\x18\x4b\x8a\xfc\x01\xdd\x06\xb6\x2f\x40\xee\xd0\x2e\x59\x93\x75\xab\x1b\x10\xac\x97\xbe\xad\x36\x2c\x33\xe3\x26\x51\xa0\xea\x27\x68\x49\xa2\xd6\xe4\xee\xd4\xda\xae\xf4\x13\x6f\x90\x78\x41\x02\x23\x9e\xdc\x8f\x5a\x5b\x3a\xb4\xfb\x52\xab\x0a\xed\x5e\xd4\xda\xd0\xa1\xdd\x9b\x5a\xeb\x3a\xc4\x07\xa6\xd6\x04\x6e\xad\x0b\xc8\x35\x71\x90\x2b\x70\xd4\xc4\x46\xae\xc0\x88\x6d\x5f\x80\x45\x53\x72\x4d\x9c\xe4\x0a\x1b\x80\xad\x36\x6c\x0d\xa6\x85\x86\xce\xca\xf7\xe4\x74\x0c\x20\x43\x82\xd5\xaf\x26\x61\x92\x7f\x36\x50\x65\x97\x9a\xe6\x0e\x08\x67\x0e\x2c\x3d\xdd\x65\x26\xbc\xbb\xd4\xfc\x36\x20\xe5\x6c\x23\xb2\xcb\xcc\x74\x77\xa9\x21\x2d\x26\xe5\x7c\x6b\xb9\x26\x2b\x07\xc6\xb2\xb0\x23\xf4\xad\xe5\x5a\xac\x1c\x18\x26\xf7\x49\xb9\x81\xb5\x1c\x18\x30\x2b\xc3\xa2\x8b\xb5\x3b\x2c\xb5\xc6\x3d\xcc\xb3\x02\x3f\xf3\x85\x30\x44\x1e\x2c\x1b\xff\xfc\x34\x8c\xbc\x64\xf4\x36\xcc\xd2\x93\x38\x03\x8e\x47\x61\x46\x5b\x7e\xe6\x53\xab\xad\x97\x68\xdd\x02\x1d\xea\x7c\xc0\xc3\xcc\x48\xda\x08\xe5\x8d\xce\x6c\x06\x81\x99\x85\x18\xb1\x7c\x8b\xd4\x98\x29\x07\x49\xa4\xc9\xf6\x19\xfa\xba\x41\x13\x0b\xe7\x36\x12\xa2\xc4\xbf\x50\xb3\xa1\x53\x6b\x0e\xa9\x52\xa9\xe4\x45\x97\x10\xe1\x0f\x04\xe4\xab\x2a\x01\xd5\x22\xeb\xb6\xde\x72\x08\xd0\xbc\x2a\x1d\x8e\x5c\x78\x96\x5e\x96\x17\x9e\x0d\x60\x4c\x70\xd6\x80\xcd\x13\x9c\x6d\x1d\x95\xf3\x74\xe4\xf9\x30\x79\x8e\x1d\x30\x8e\xb1\xa4\xed\x58\x5d\x85\x93\x20\x82\xec\x2e\xd4\x21\xcb\x6a\x38\x35\xa5\x27\x2f\x33\x9b\x4b\x31\x59\xc2\xea\x96\x65\x74\x0b\xe1\xec\xa2\x0d\x24\x8b\xef\xf7\x3b\xbf\xb5\x4b\x1d\xdf\xec\x27\xb2\x5d\x38\x8a\xed\x5a\x9c\x49\x50\xd1\x19\x6c\x57\xb8\xeb\xed\x2a\xc7\xab\xdd\x85\xcf\x55\x94\x42\x76\x95\x33\xd5\xae\xf3\x30\x35\xdf\x14\xee\x88\xde\x84\xd3\xc9\x65\x19\x2c\x02\x18\x6c\xb5\x28\xbb\x31\xd7\x26\x48\x61\x53\x83\x71\x1c\x15\x33\x28\x30\x25\x20\xa5\x72\xed\x02\x3c\xba\xcd\x20\xe8\xe7\x73\x83\x48\x68\x3d\x93\xd6\x18\x9a\xf0\x55\xb1\x8b\x82\x9f\xb7\xf4\xf6\x1f\xc9\x16\x71\xc3\x7a\xe5\xda\x43\x37\x1e\xfa\x62\x4b\xf3\x51\xa9\x5c\x83\x67\xe7\x0d\xfc\xfb\x25\xcf\xd6\x7e\x6b\xc0\x69\x14\xc3\xa9\x5c\x57\x7f\xaa\xdc\x54\xa9\x3b\xf9\xff\x25\x0f\x5f\xaa\xd5\xea\xff\xcf\xde\xdb\x6f\xc7\x6d\x23\x0b\xe2\x7f\xc7\x4f\x81\x99\xdf\x19\xbb\x15\xb5\x24\x02\xfc\x02\x6d\x2b\xbf\x4d\x14\xfb\x3a\x1b\x3b\xf6\xb1\x9d\xb5\xef\xf1\xb1\x33\x20\x09\xaa\x19\xb7\xba\x75\xbb\x29\xab\x35\x13\xcf\xd9\xd7\xd8\xd7\xdb\x27\xd9\x83\x02\x48\x82\x24\x00\x76\xcb\x72\xee\x64\xc6\x9a\x33\x4e\x77\xb3\x50\x55\xa8\x2f\x14\xf1\x51\xb8\x67\xc3\xe6\x8f\x62\x13\x88\xfe\x21\x30\xb6\xac\x59\x70\x05\xe3\xb8\x6e\x03\x06\xe0\xed\x6a\xef\xf6\xe4\x1f\xc0\x9c\x1d\x63\xb8\x8d\xcc\x84\xd0\x7e\x6b\x51\x59\x70\x41\x2a\xb1\x99\x2e\x8c\x98\x36\xf7\xef\x2f\x80\xab\xcd\x37\xdf\x7c\x33\xf1\xc9\xc1\x42\x67\x4a\x7e\x70\xee\x86\xa9\x37\xc3\xc8\x7b\xe0\xb6\xdb\x0c\x63\xbd\xed\x47\xed\x6f\x81\x3d\x4f\xf5\xe7\x6a\x29\x23\xd3\x10\x8d\xe5\x7e\x1e\x0b\xf4\x75\x2f\xe6\x51\x9e\xd1\xee\x64\xa9\x27\xf0\x26\x77\x14\x8b\x77\x0c\xbb\x70\xec\xad\x2e\x6b\x6e\x4d\xdb\x6d\x86\x93\x83\xbd\xad\x36\x35\xc0\x76\x5b\x95\x6a\xe5\x3c\x7a\xf2\xed\xc9\xef\xa0\x1a\x47\xf3\xf7\xfc\x0a\x9a\xae\x79\xb6\xe2\x95\xe5\xee\x24\x8b\x42\xe1\xca\xc1\x1b\x54\xa8\xbc\xc8\xb0\x51\xcd\xa3\x33\x96\xb5\xea\xd1\xb7\x58\x19\x34\xd4\x01\x1e\x6a\xe9\x8c\x65\x06\x4d\x7d\xf5\x51\xae\x03\x5b\xb6\x46\xd5\x90\xe6\xdb\x89\x3e\xbe\x9d\xc6\xf1\x97\x2d\x4e\xff\x0a\x47\x56\x3e\xf7\xd2\x7d\xaf\xb0\x9a\x46\xd8\x5a\x32\xed\xc5\xa3\x6f\x0f\xf0\x16\x2b\x19\xc3\xbb\xaa\x6f\x72\xfd\xe2\x18\x4e\x9f\xb6\x4b\x18\xe5\xa2\xac\x26\x86\x02\x54\xdd\x25\x0d\x5e\x64\x39\x4b\x69\x62\xa8\xcd\xe4\x6d\x12\x9a\xb2\x3c\x2b\x78\x67\x8d\xc3\x04\x98\xf9\x39\xe1\xb8\xf0\xba\xcf\x3e\x7d\x09\xc4\x96\xa1\x9b\x93\xef\xe1\x0c\xfa\x00\xc1\x36\x73\xcf\xe6\xe9\x62\xf1\x28\x35\x4f\x16\x43\xc2\x68\x9e\x2a\x86\xd7\x55\xf3\x44\xb1\x78\xc4\x9b\x69\xe2\x01\xa7\xd6\x79\x62\xeb\x9c\xb0\xe5\x6d\x01\xe6\x7d\x90\x3c\x61\x6a\xa9\x05\xf3\x4a\x26\xfe\xdd\x12\x18\xdd\xb3\xa7\xf5\x5f\x3d\xa1\x64\x46\x54\x9f\x73\x78\xf5\xa6\x44\x07\xc8\x7f\x8b\xde\xa9\x8f\xb4\xfd\x88\x03\xed\x73\x64\xbb\x3b\x52\xb1\x34\x59\xc0\xe1\x58\xf9\x6e\x09\xaf\x0f\x3e\x36\x97\xa9\x31\xbf\x09\xc1\xd4\xd2\x84\x09\x24\x21\x20\x61\xf2\x4d\x26\x86\x03\xb2\x1c\xed\x03\x21\xdb\x44\x23\xba\x8f\x88\x67\x95\x1a\x4c\x9b\x4d\x26\x29\xba\x8d\x32\x99\xe7\x8a\x8f\x39\x60\xf6\x36\x21\x93\xab\xb0\x23\x53\x7c\xe8\x3e\x0a\xc6\x48\xa4\xe8\x1d\xca\xd0\x3b\x94\x4b\xcc\x11\xcf\x13\x9e\x32\x53\xd1\xa1\x1e\xe6\x68\x07\xe6\x25\xef\xe2\x53\xa6\x7a\x71\x80\xbc\x4d\xec\xf1\x20\xf0\x49\x60\xa7\x75\xf4\x75\x43\x8e\x7a\x7b\xe8\xeb\xa3\xad\xfb\x22\xf0\xfb\x61\x92\xfb\x9c\xf4\x67\x79\x90\x45\xa5\xc2\x5f\x72\xd3\x74\x1f\x3a\x46\x99\x69\x8a\x0f\x01\xc9\xfb\xf7\x91\xef\xa9\x5e\x82\xfa\x8d\x77\x8b\xa2\x63\x64\xe2\x83\x6d\x77\x5a\x6b\xab\xc9\x40\x35\x89\x56\x4f\xb6\xb1\xfe\x09\x6f\xd4\x99\x08\x84\x09\xc3\x41\xe5\x13\xd4\x99\x04\x84\xc9\xc2\xcc\x0c\xe3\xeb\x13\x85\xb9\x19\x26\xd0\x27\x09\x79\x1f\xe6\xcb\x04\xdf\x3f\xeb\x04\x9f\xc8\x85\x0f\x8b\xf9\x72\xb9\xd2\xe7\xdc\x8e\x60\xa0\x56\x7f\x9f\x44\x04\x6a\x21\xb4\x98\x47\xe6\xe9\x06\xd3\x74\x9f\x69\x86\x6e\xc7\x79\x20\xe3\x74\xdd\x1f\x71\x36\xe8\xcb\x14\xc2\x60\x32\x40\xa4\xcf\x3b\xcd\x1e\x40\x03\xd7\xc4\x41\x37\x21\xef\xce\x19\x88\x67\x5f\xa6\x0b\x6e\x74\xba\x00\xf4\xb1\xc5\x4c\x81\x59\x2d\xed\x24\x81\x52\x8d\xfd\xd8\x94\x00\xb0\x4f\x0b\xd0\x3f\x74\x81\x8d\xf5\x8c\x91\x30\xfa\xdc\xb5\x31\x14\x95\x7f\x9f\xe9\x83\xc1\xf4\x80\xfe\x0e\x4f\xc2\xa8\xf3\x16\xaf\x9d\xc2\xee\xcf\x0a\x10\x12\x6c\x37\x2f\x20\x00\x3b\x38\xe1\xbb\x44\xfe\xbb\xce\x0d\x64\xd8\x0b\x13\x9e\x53\xf1\xca\xef\x47\x71\x96\x87\x5e\x0c\x9f\xbd\xd8\xcb\x73\x0c\x9f\x8b\xd8\xe3\x61\xe2\x9b\xe7\x0c\x8a\x22\xf3\xbc\xd4\x87\xc9\x85\x88\x86\x14\x87\x58\x7e\x0e\x8a\x84\x16\x0c\x10\xa4\xbc\x60\x41\xc1\x82\x1d\xa6\x0b\xb6\xca\x3c\xb5\xb0\xaf\x44\xa7\xb5\x74\x9c\xa2\x85\x88\xda\xa4\x33\x07\xc7\xc3\xe4\xc5\xb2\xb0\xf4\x65\x88\x1e\x19\x71\x09\x09\x76\x1d\xa4\x45\x93\x91\x61\xba\xe3\x1d\x83\x81\x9a\x10\xf3\x21\xf6\x2f\x43\xf5\x27\x0c\xd5\x42\x2b\xdb\x0d\xd6\x46\xe5\x74\x86\x6b\xa9\x20\xe7\x80\x4d\x48\xff\xa8\xb3\x76\xae\x59\x0d\x47\x77\xe3\x44\x0c\xe0\xc9\x97\x79\xfd\xff\x9e\x81\xf9\x8f\x77\x2c\xef\x07\x79\x89\x43\xf9\xb7\xe6\x54\x2e\x5a\x2d\x2f\x16\x39\xca\xba\xe7\xf5\xb4\x1e\x3c\xea\x5f\x9d\xf2\x63\x77\x19\xa0\x9e\xa8\xe5\x2d\x0e\xf9\xc4\x94\xc1\x20\x7d\x49\xb9\x5c\x3f\x5b\x95\x67\x7c\xb2\x30\x0e\x63\xeb\xff\x5a\x55\x3f\xd5\xef\xf9\xe2\xcb\x64\xd1\x7f\xcf\x6c\x26\x82\xa5\x3a\xd1\x31\x22\xf7\xea\xcf\xf7\x8f\x25\x86\xfa\x07\xc7\xdc\xf0\x9f\x26\x0b\xf4\x17\x05\xb6\x67\x9d\x2f\x54\x3e\x5a\xb0\xf9\x9a\x8f\xef\x0a\xec\xcf\x8f\xd5\xef\xe3\xab\x8b\xee\x1b\xae\x41\x2c\xa7\xbc\x7a\xb8\x62\xf0\x99\xcd\xbf\x2b\xab\xb5\x41\x40\xcd\x12\xfe\x02\x1d\xa0\xc9\x02\x2a\x7b\xee\xa1\xaf\x3b\x93\x1f\xfd\x99\x2c\x8d\x56\x3d\x4b\xad\x57\x66\x87\xdf\x40\x21\xbd\xfa\x3d\x97\xb3\x72\xce\xd1\x44\x3d\xbb\x8f\xd4\x96\xcc\xbe\x14\x5b\x6d\x5a\x05\xdd\xa0\xa0\x56\x29\x3f\x7a\x23\x81\xa0\xec\xe8\x40\x10\x60\x0b\xe7\xcb\xcb\xc9\x62\x8a\x30\x3a\x42\x64\x6f\x8b\x8a\xed\x08\x6e\x42\xd9\x05\xad\xbf\x67\x2c\x9e\x2d\x51\xec\xef\x8f\x4c\x85\x2e\x3a\x10\x75\x86\x34\x69\x71\x5e\x7f\x8d\x4d\x24\xde\xdb\x65\xd3\xc3\x0c\xfd\xb3\xaf\xb4\x3d\x3a\x5c\xcf\xcb\x8c\x4f\xbc\xbd\x2f\xab\x5e\x5b\xaf\x7a\x0d\x1e\x15\xf0\x28\x34\x3d\x3a\x85\x47\x83\x05\x23\xc8\x59\xe0\x51\xfc\xc9\xcb\x68\x91\xa3\xd6\xfd\xef\xbd\x8c\x76\xca\xce\xce\x98\xb7\x69\x16\xd3\xf0\x40\x28\x43\x68\xd8\x68\x3c\xa9\x5b\xde\xbf\x8f\x88\x5c\xf4\xaa\x7f\xf9\xe6\x9b\x6f\x50\xbc\xb7\x87\xd0\x3b\x33\xa6\xee\x5f\x07\x13\x0e\x06\x98\x30\xdd\xdb\xdb\x0e\x53\xb7\x9d\x6f\x0c\x2f\x9d\x9e\xe0\xb6\xdf\xc6\x4d\xf2\x5d\x60\xad\xdb\x58\x32\xab\x75\x1b\x6f\xea\x7a\xd3\x5b\x32\xdb\xc5\xe4\x0f\x31\x25\x3b\x76\xbb\x6e\x67\xbe\x93\x00\xb5\x86\xa3\x94\xb8\xaf\x7a\x0e\x45\x7e\x55\x0f\xf7\x9d\x0b\xa6\xb6\xd5\xcf\x0c\x4e\x35\x4e\x38\xba\x8d\x0a\xd8\xec\xf6\x0f\xf1\xf1\xd4\x76\x85\xcb\x19\x83\x0a\x73\x0c\xdd\x46\x29\x80\x33\xb9\x3a\xf8\x0e\xa9\x75\x42\x13\xff\x90\xac\x94\xa7\x82\xf1\x66\xa9\x55\x2d\xb6\xa9\xb5\x56\xb9\xf5\x4f\x3e\xc1\x89\xf6\x04\xfb\x9d\x47\x9d\x46\xe6\xb1\xad\x21\x06\xf7\xd4\x4c\x38\xd8\xb8\xac\x9c\xcc\xa1\x5d\xa4\x30\xca\x27\x58\x7b\x82\xb1\xfe\x28\x96\x3b\x5b\xe5\x23\x12\x9a\x47\x3c\x58\x40\x16\x94\x66\x68\xbf\x26\xbb\x2f\x84\xba\x2f\x2f\x7a\xb3\x2e\x1e\x43\x43\x82\x8e\x6b\xc1\xec\x0b\xd1\x9a\x28\x88\xc0\x75\x6a\x40\x20\x62\x5d\xbf\x4e\xbb\xf8\x13\xe1\xd1\x94\x7e\x41\xed\x4c\xb8\x2d\x01\x9b\x96\xf9\xd0\xc8\x12\x69\xbf\xda\x3a\x1a\x59\x0e\x9d\x54\x42\x10\x15\x31\xd1\xfa\x77\x59\x1a\x95\x30\xa1\x82\x81\x92\xe1\x85\x19\x26\x52\x30\x50\x12\xfc\xd4\x0c\x13\x2b\x18\xf0\xf9\xd9\x97\x65\xd8\x2f\xcb\xb0\x5f\x96\x61\x87\xd9\xe6\x97\x65\xd8\x7f\xca\x39\xde\x30\xda\x79\x8e\x37\x8c\x46\xe7\x78\xf5\x77\xb6\xe1\x1c\x6f\x18\x7d\x99\xe3\xbd\xf1\x39\xde\x30\xda\x76\x8e\xd7\xa4\x9c\xee\x1c\x2f\x28\xc8\xbd\x69\xbb\x59\x3b\x33\x2f\xcd\x52\xef\x0f\xbd\x34\xbb\x89\x82\xdf\xe5\xe2\x82\x86\xce\x97\x59\xe0\xee\x2c\xf0\x26\x82\x35\xd5\xc3\x4d\x14\x68\xbf\xbf\x8e\x02\x55\xa5\x1b\x20\x0e\xb5\x3a\xd1\x3b\xd5\x74\xd3\xfa\xf7\xfc\xd1\xd3\x5f\x9e\x3e\x7c\xf8\xe2\xc1\xcb\x17\xfd\xd9\xe2\x67\x3f\xfc\xf2\xc3\x4f\xdf\x3f\x78\xfd\x60\x78\x2b\xf7\xf3\xa7\x3f\xff\xf4\xfd\x2f\x27\x4f\x7f\x7a\xf1\xf2\xdb\x9f\x9a\x96\x1a\x39\x39\xad\x7c\xb2\xdd\xb4\xb2\xd6\x62\x35\x5b\xd6\x45\x5b\x7a\x73\xd2\x35\x69\xf1\x76\x8d\xa7\xe8\xca\x56\xaa\xbc\x92\x53\x22\x15\xba\x8f\x48\x70\x0f\x55\x86\x29\x11\xad\xcf\x6f\x36\x68\x1f\x85\xe8\x6b\x74\x25\x4f\x0f\x56\xf5\x21\x4d\xf8\x44\xf6\x60\xa6\x12\xfd\x05\x45\x83\x5c\x04\xd2\x40\x7e\xf9\x1a\x1d\xa3\x2b\xf4\x17\x14\x9a\xb2\x44\x7e\xf9\x9f\x02\x2b\x41\x5f\x23\x41\xc7\x17\x74\xf6\x0c\xc0\x1b\x39\x2d\xf7\xba\xf7\xf3\x95\xfc\xf9\x3f\x2d\x53\xc1\x9a\xd8\xce\x4b\x54\xc2\x75\x02\x06\xa1\x35\x92\xd9\x48\xc9\x6c\xe4\x01\xcd\x8d\x41\x30\x0d\xa8\x94\x2e\xba\x92\xa0\x57\x96\x69\xa5\xd6\x40\xba\x62\xbc\x82\x0b\x7e\x86\xbd\x16\x72\xed\x77\xfd\xe3\x68\xdf\x7a\xab\x1c\x5d\x6b\x78\xfc\xf0\xc5\x73\xc1\xeb\xc6\xc3\x26\x63\xd0\xef\x9d\xb0\xcc\x8f\x09\x30\x20\x51\x1b\xeb\x93\xf5\x65\xcf\xb6\x8c\x60\x8f\x6b\x30\x8b\x08\xd5\xcd\x13\xbf\xa2\xfb\x28\xbe\x87\x7e\x75\xcc\xcc\x41\x1f\xe0\x68\xaa\xb9\x2a\x4a\x4d\x3e\x2d\xab\x67\xcb\x35\xd4\x71\x15\x56\x05\x97\xe5\xfe\xba\x87\x0e\x90\x69\x37\x75\x8d\x5c\x6f\x74\x1f\xa9\x7a\x11\x26\x60\xf1\x37\xe8\xe0\xbb\x63\x04\x64\x34\x2c\x16\x5a\xdd\x1d\xd5\x3a\xd5\x6f\x8e\x81\xac\x7d\x73\xf5\x80\xf2\x13\x8d\x72\x07\xd5\x81\xe1\xbd\xa7\x61\x60\xbb\xa9\x25\xcd\xb0\x16\x7c\x53\x81\x01\x8d\xa8\x85\xda\x77\xa2\x1f\x1d\xa1\x67\xab\xf2\xac\xac\xca\x0f\x1c\x9d\x2f\xe7\x57\x8b\xe5\x59\xc9\xe6\x68\xf9\x81\xaf\xd0\x7f\x3c\x9c\x90\xbd\xbb\x68\xf3\x8e\xa2\x7d\xb4\x79\x17\xc1\xbf\x21\xfc\x1b\x88\x30\x63\x46\xa9\x2c\x5a\x92\x97\xe7\x07\xde\x21\x6f\x13\x3b\xb6\xcc\x5b\x98\x53\x18\x8e\x8d\xf6\x31\xb2\xe8\xd5\x0b\xf0\x72\x8e\x4f\x0d\x3f\x75\x81\xb1\xbe\xce\xa6\x03\xfb\xd9\xdb\x75\x35\x65\x0d\xfe\x53\xf1\xb3\xf3\xe5\x8a\xad\xae\x3a\x37\xd1\x09\x17\x78\xa9\x0f\x44\xd6\x55\x4a\xe3\xad\x33\x66\xef\x7f\x69\xec\xd9\x18\xdf\xbd\xb5\x1d\x7f\xbb\x95\x1d\xbf\xb3\xae\xe3\xbb\x56\x75\x6e\xfe\x2a\x81\xe5\x45\x75\x7e\x51\x3d\x86\x57\xeb\x0e\x2c\x82\x24\x3d\xe7\xeb\x72\xc5\x73\xed\xa2\x81\xb4\xac\xd6\x75\x41\x68\xd9\xb8\xf3\xb6\x50\x37\x7e\xba\x98\xd7\x6a\xd2\x6a\x70\xb3\x15\xbf\x8b\x08\x09\xa6\x88\x84\xd1\x14\xf9\x34\x98\xa2\x10\x93\x7e\x63\x75\x67\xc1\x5d\xf1\x4c\x7f\xd4\xbf\xb4\xa0\x7e\x69\xb6\xde\x5b\xa0\xf7\xae\x87\xed\x1a\xf7\x17\xc0\x4c\x2d\xdc\x84\x58\xbf\x7b\xd7\xdf\xde\xbc\xb5\x44\xfb\x2d\x4c\x4d\xfc\x01\x1e\x69\x72\x0b\x7e\xd9\x98\x1d\x2c\xc2\x8d\x95\x12\x00\x4e\x9a\xdb\x7a\x61\x04\x88\x3c\x0f\x1d\x20\x31\xd0\x36\x37\x25\xe8\x92\x10\xd9\x8b\x4f\x3e\xd7\x8a\x9e\x61\x62\xce\x20\x34\xe3\xe4\x59\xdd\x89\xc7\x6c\x01\x73\x3f\xbd\xae\x1d\x21\x62\x9a\x43\x4b\xd7\xcb\x55\x3a\x2e\xff\x1e\xfa\x4f\xa9\x24\xf8\x84\x94\xa8\xbb\x28\x26\x64\x6d\x9d\x36\x7f\x42\xe0\x0e\xfa\x3e\xb8\x88\xf5\xae\x62\x16\xd6\x2b\xa8\x05\x79\x67\x3d\x41\xd2\x29\x24\x48\xae\x53\x41\x90\x74\x4a\x07\x92\xeb\xd7\x0c\x54\x0c\xe3\x31\x8e\x71\x97\x65\x7c\x2d\x9e\x71\x97\x69\xbc\x0b\xd7\x46\x3d\x48\xe3\x6a\xa6\x46\xca\x45\xb5\x94\xd6\x6c\xd6\xf4\x9c\xc1\x64\x5e\xed\xce\x06\x51\x08\x88\x43\xb8\x6f\xf6\xdd\x31\xc8\xc5\x06\x33\x5f\x5e\x22\x05\x33\xbe\x1a\xf1\x5c\x0c\xb0\x6b\x8b\x0f\xc8\x44\x19\xfc\x40\x7e\x94\x49\x2f\x7c\xb6\xbb\xc0\xcb\x19\xaf\xd8\xf0\xc9\x0e\x6f\x0d\x1a\xb2\x27\xa5\x78\x05\x99\x5f\x9c\x2d\xa0\x73\x06\xb7\xaa\x25\x58\xa7\xd9\x53\xd4\x66\xd2\x46\xe0\x1d\xdf\x49\x74\x1a\x1d\x2d\xb5\x6f\x28\x16\x42\xe2\xaf\x4e\x3d\x1b\xed\xb9\x60\x1f\x6b\xb0\xf3\xe5\xa5\x35\x2f\xb5\x4a\xeb\xa5\x31\xcf\x31\xf5\xe4\xa5\xd0\xc2\xcb\x37\x1b\x1b\xef\x2f\x37\xd2\xd6\x8e\xa1\x07\x76\x20\x30\xb6\x63\x60\x7d\xbb\xdd\x37\xd7\x33\x03\x47\x58\x6d\x7b\x14\x40\x97\x26\x42\x2f\x01\xbc\x1e\xba\x16\xcb\x5f\x6e\x70\x0b\x8e\xb7\x01\x97\xf6\xf5\x72\x83\x5d\x7a\x54\xb0\x8f\x1b\x58\xd0\xa3\xd3\xbc\xd7\x17\x2b\xf0\x28\x79\x9d\x88\x30\xf5\x71\x2b\x7f\xb9\x09\x54\x2c\x40\x93\x89\xe2\xad\x39\x1a\xac\xe8\xab\xf3\xc1\xb6\xd7\x1b\xc0\xf6\xb8\xc1\x26\xa3\x86\xc4\xf6\xb8\x87\xed\xc9\x38\xb6\xdf\xd5\xa9\x3a\xa1\xd0\x61\x9f\xa8\x1f\x12\x2d\x66\x8a\x76\x7a\xdb\x7b\x3e\x5b\xa2\x67\xa5\xc3\xb2\x05\xc9\xfa\xce\x47\x7c\x4f\xfb\x2a\x53\xb9\xe6\xfb\x27\x9b\x7c\x47\x72\x0d\x5a\x97\x19\x0b\x20\x69\x41\x63\x01\xa9\x86\x7e\xdc\x42\xdb\x43\x12\x0c\x16\xb3\xe5\x53\x99\xa5\x1c\x77\xe6\xc3\x74\xbe\xac\x9d\x7d\xbe\x84\x44\xcf\x11\xe2\xc5\x0b\x74\x4b\x62\x74\xe2\x41\xf3\x95\x49\xdd\xe9\xfb\xf7\x5b\x26\xc1\xb4\xeb\xfe\xc1\x55\x9a\x3e\x41\x07\xda\x73\x9b\xa1\xa3\xae\xeb\x34\x38\x8c\xc8\x9f\xec\x88\xbc\x3b\xe7\xd1\x76\x77\xab\x19\x8f\x7e\x97\x15\x57\x1a\x1a\x98\xed\x18\x32\x17\x05\xd7\xee\xf9\x93\x11\x1a\x8f\x77\xa4\xe1\x1a\xdb\x56\x6c\xb1\x3e\x5f\xae\x9d\x56\x02\xe1\xf7\x59\xf9\x58\x3a\xc6\xcb\x37\xda\x84\x62\x6b\x87\xd6\x31\x4f\x36\xdc\x66\xe0\x53\x90\x63\xa3\x9f\x35\x7e\x9c\x97\x88\x55\x30\x04\x42\xbc\x34\xe7\x84\x2f\x3d\xe8\x83\xb1\x68\x6b\xf3\x72\xe4\x35\x01\xc0\x08\xf7\xd2\xab\xbb\x23\xa1\x6d\x2e\xff\xd2\xab\x3b\xa3\xe0\x2c\xe3\xd6\xd1\x11\x3a\x99\xb9\x82\xdf\xf6\xc3\xfa\x35\x87\x8c\xf1\xd0\x88\xb4\xf0\x55\xc7\xe1\x66\x5c\x19\x31\xee\xdd\x42\x6a\xdd\xea\x65\x63\x70\xdb\x37\xd9\xe0\xa6\xd1\x44\x4b\x42\xf6\xb6\x19\x00\x25\x02\xd2\x43\x40\x06\x08\x9c\x52\x14\xb9\xc7\x6a\x79\xe9\x10\xe2\x5c\xf3\x86\x97\xad\x6b\xbc\x43\x93\x7f\x28\xf6\xe5\x0f\xb7\x6b\x66\xe0\xab\x2b\x7e\xcc\x35\xaf\x79\xd9\xba\x90\x8e\x11\x7e\x68\x31\xce\x97\x97\x9f\x3e\x41\xfb\xc3\xd2\xf4\x46\x32\xd0\xb7\xd5\xd3\x3a\xd3\x90\x62\x7c\xeb\x4d\x66\xc2\xf3\xd1\x97\xb6\x0e\x16\x9b\x23\x76\xf2\x95\x6e\x0b\xe1\x92\x8e\xc5\x8e\x7f\xae\x6d\x51\x86\x49\x9a\x1b\xdf\x15\x35\x80\x6f\x66\x7c\x44\xbb\xe1\x34\xd0\xd7\x30\x79\x35\x9c\x07\xba\xee\x5e\x2a\x7c\x9d\xad\x54\xb0\x49\x2a\xe3\xe5\xbc\xbb\xdf\x09\xef\xa1\xa3\x2e\xff\x7b\xe8\xeb\xfe\x0f\x40\x1c\x16\x68\x9a\xdd\x5c\xff\x24\x9b\xa0\x3e\x79\x0e\x4f\x9f\x66\xac\x99\x37\xce\x41\xa2\x23\xa3\xea\x75\x90\x7a\x16\x70\x88\xf3\xc8\xb8\x99\xee\xc5\x7f\x5d\x70\xfe\x37\x3e\x44\x3a\x63\xeb\x59\x6d\xdc\x5b\xdd\x45\x3f\xe0\xe2\x53\x26\x0b\xc7\xe7\x84\xb6\x4f\xe9\x6d\xe9\xfc\xee\x73\x88\x2d\x3d\xfb\xac\x9c\x96\x1a\xaa\x89\x39\x3d\xe1\xdc\x69\x6e\x4e\x43\xa5\xa6\xe7\x74\x54\xd7\x9d\x57\x6c\x45\xe1\xee\xc4\xe3\x41\x27\x1e\x5f\xb7\x13\x8f\x07\x9d\x78\xbc\x5b\x27\xcc\xaa\x92\xa6\xab\x9c\xac\x5a\xa2\x15\xaf\x56\x25\xff\xc0\x0d\x1b\x10\x91\x3a\xdc\x2d\xe3\xc1\xf9\xc5\x7a\x56\xb3\x61\x12\x91\x01\xf2\xc9\x10\xf2\xd3\xcb\x13\x1b\x4e\x0f\x35\xa4\xa7\x43\x17\xb6\x9e\x27\xba\xa1\x5d\x93\xf6\xf8\xa5\xb6\x50\x1a\xc2\x59\x73\xd8\x69\x8b\x08\xb1\xe5\x62\x4e\xfd\xb1\xdd\x9f\xe9\x14\xfb\x97\xed\x9a\xd7\xdc\xae\xe9\xef\xba\x59\xd3\x1f\xdb\xaa\xe9\x3b\x36\x6a\xfa\x5f\xb6\x69\xde\xf4\x36\x4d\x7f\xcb\x4d\x9a\x06\xb5\x74\xb6\x68\xfa\xdb\x6c\xd0\xf4\xed\xc7\xf0\x9b\x8d\x87\x77\x69\xf0\xf1\xed\x94\xe2\x7f\x91\xed\x9a\xfd\x02\x3b\x21\x26\xbf\xdb\x1e\xce\xba\xdc\x8e\xa0\xf9\xc7\x2a\xb7\x73\xad\xdd\x96\xea\x71\xbb\xdb\xb3\x86\xd9\xa9\x20\x4f\x88\x49\x67\x5b\x48\x88\x89\x75\x9b\x09\xdd\xb2\x20\x8f\x00\xec\x6c\x35\xa1\xaa\xaa\x45\x88\xc9\x8d\x1d\x21\xd6\xbb\x6f\xad\xc9\x33\xd8\xe4\xe0\x6d\xb2\x34\x4d\x93\x3c\xcc\xa7\x5a\xc1\x9e\xbd\xa9\x09\x32\x22\x09\x23\x09\x61\x7a\x39\x9f\x3d\x43\xdd\x1e\x43\xd3\x04\x87\x89\x87\x43\xa6\x57\xff\x31\x13\xc1\x21\x29\x78\x26\x6b\x06\xd5\xb5\x81\xb6\x24\x12\xc5\xbe\x4f\xa2\x48\x96\x15\x52\x95\x83\xcc\x44\x28\x4f\x83\x80\xd1\x58\xaf\x2b\xb4\x25\x91\x3c\xf5\x32\xc2\xbd\x5c\x2f\x43\x64\x26\x12\xc4\x69\x18\x50\x9c\xeb\x45\x8a\x7a\xa9\xe9\x4d\x57\x29\x12\xf6\x74\xcd\x2a\x45\x38\xfa\x52\xa6\xe8\x86\x72\x22\xba\x73\x99\x22\xd1\x64\x2c\x2f\xd2\x63\xc6\x30\x33\xa2\x5f\xca\x14\xdd\x7c\x6e\x44\xb7\x2d\x53\x64\x54\x4e\x37\x3f\xa2\xa3\x65\x8a\x7c\xea\x2e\x53\x24\x86\xf1\xbb\x94\x98\xb2\x25\xf2\x2f\x92\x2d\xfd\x4b\x1f\x6e\xb9\xd9\x83\x2d\x9f\xe9\xc8\xca\xf5\x93\x28\xf9\xa8\xe9\xae\x42\xf4\x4b\xbd\x83\xd7\x70\xd7\x4d\x77\x93\xef\x21\x3b\x3f\x9f\x5f\x4d\xd4\x8f\x53\xc4\x56\xa7\x17\x67\x7c\x51\xad\xfb\x77\xf2\xe8\xc7\x67\x5a\x7e\xa0\x94\x52\x4b\xa2\x47\xde\xdb\x04\x84\x32\x52\x24\x90\x57\xe4\x31\xa1\x8c\x13\xb2\x37\x1d\xc2\xc5\xd8\x8f\x83\x20\x81\x32\x83\xc4\xe7\x45\x14\x66\xb9\x9e\x1a\x0c\x1a\xa4\x61\xe6\x15\x69\x56\xc0\x05\x08\x59\x90\xfb\x29\x29\x4c\x88\x79\x92\x86\x79\xca\x42\xb8\x3d\x1b\xd3\x24\x4f\xd3\xcc\x89\xd8\x4f\xc2\x28\x23\x61\x0a\xe9\x8c\x1f\xd0\x34\xf4\xa9\x09\x71\x98\x14\x18\xe3\x02\x38\x4e\x23\x2f\xcc\x3d\x9c\x38\x11\x27\xc4\x2f\x28\x61\x70\xe5\x36\x2b\x70\x12\x14\x49\x6a\x42\xcc\x52\x9c\x85\x3c\x07\x8e\x73\x16\xe5\x14\x63\xea\x44\x9c\x53\x2f\x66\x4c\xca\x98\xf9\x9e\xef\x91\xc0\x28\x63\x4c\xa8\x1f\xa6\xf2\xce\x88\x20\x8c\xbd\xa8\x48\xb9\x13\x31\x09\x7c\x4c\xc3\x14\xee\x8e\x08\x38\x0f\x52\x42\x33\xa3\x28\x42\x2f\x8b\xf3\x0c\x2e\x10\xcf\xc3\xa2\x48\x03\x4e\x9c\x88\x63\x92\xf2\x30\x8f\x41\x14\x05\x89\x53\x9a\x44\x46\xe5\x51\x2f\xe7\x29\x96\x97\x57\xf8\x29\x8e\x92\x28\xc5\x6e\x19\xa7\x79\xe6\x45\xb2\x42\x25\x09\xb3\x18\x13\x3f\x34\x21\xce\x70\x92\x16\x58\x32\x90\x15\x51\x42\xa2\x24\x70\x22\xe6\x41\x92\x46\x49\x06\xb2\x4b\x78\x81\x03\x96\x1b\x65\xcc\x8b\x94\x07\x31\x85\x6b\xc4\x7d\x1a\x14\x24\xe4\xbe\x13\xb1\x57\x64\x38\xc9\x33\x68\x40\x53\x9a\xe5\x61\x6a\xe4\x98\x04\x5e\xc6\x70\x96\xc1\x25\xed\x31\xcb\x92\x2c\x0a\xdd\xca\xcb\x79\x42\xb2\x08\x1c\x24\x4c\x48\xea\x91\xd8\x88\x38\x60\x71\x40\x03\x06\xef\x08\x11\x67\x11\x0f\xa8\x9b\xe3\x30\x4b\x3d\x96\xe4\xc0\x49\x9a\x07\xb8\x48\xf3\xc0\xe8\xd2\x51\x91\x50\x9a\x03\x62\xea\x63\x1c\xfa\xa9\x9b\xe3\x84\xfa\x3c\xc4\x21\x01\x97\xe6\x51\x94\x17\xcc\xec\x20\xd4\xc7\x59\x14\x41\x86\x4f\xf2\x34\xf0\x09\xf6\xdc\xb1\xc2\xf3\x7c\x12\x67\x54\xde\xf9\x5e\xa4\x04\xfb\x46\x73\x4b\x8b\x30\x89\x8b\x4c\xd5\x37\xe5\x85\xc7\xb9\xdb\x2a\xb2\x88\x7b\x5e\x5a\x80\xe1\xfb\x39\xa3\xb4\xc8\x8c\x56\x91\x87\x2c\x4e\x70\x00\x88\x13\xdf\x63\x2c\x26\x6e\x51\x78\x51\xc6\x22\x3f\x94\xd7\xbb\x78\x9e\x4f\x89\xd9\x41\x70\x40\x12\x92\xc8\x77\x2f\x8f\x79\x3c\xe2\xb1\x5b\x14\x24\x4e\x63\x8f\x51\x08\x2e\x41\x94\x13\x52\x14\x46\x97\x26\x1c\x0b\x31\x81\xc8\xc2\x8c\x44\x59\x42\x22\x27\xe2\x20\x27\x59\x94\x17\x60\x15\x21\xcb\x02\xc2\x78\x6e\x8c\x15\xbe\x4f\xbd\x1c\x83\xc8\x92\x3c\x09\x53\x3f\x2f\x9c\x88\xa3\xd0\x63\xb1\x1f\x06\xd2\x41\x58\x11\xf9\x39\x37\x9b\x5b\xc4\x3c\x96\x42\xdc\xf6\xb3\x38\x4e\x09\x73\x87\x4d\x8a\x33\x92\x25\x44\x46\xb7\x98\xe7\x8c\xf3\xc8\x84\x38\x21\x31\x21\x99\x14\x19\x0e\x28\xf1\x43\x3f\x75\x22\x66\x24\x2d\x38\x65\x32\xce\x66\x05\xf6\xfc\xc8\xe8\x20\x8c\x62\x16\x45\x01\x70\x9c\x66\x01\xf1\x3d\xcf\x1d\xdd\x32\x12\xa4\x34\x8d\x3d\x88\xb3\x5e\x41\x93\x38\xc1\xc6\xe8\x16\x47\x59\x88\x19\xc8\xd8\x8b\xc2\x20\xe5\xbe\xdb\x2a\x72\x9c\x10\x4e\x71\x02\x88\x23\x5e\x84\x04\x1b\xc7\xbc\x3c\x4a\x12\x2f\x22\xa0\x8b\x30\x8c\x42\x96\x8c\x78\x5e\x11\x78\xdc\x0f\xa5\xec\xc2\x38\xc6\xc4\x23\xcc\x68\xc7\x5e\xc4\x98\x27\x7b\xe6\x93\x34\xcd\x71\xea\x56\x1e\x4e\x58\x90\x61\x0c\x61\x33\xa5\x39\xc9\xbd\xcc\xc8\x31\xe6\x7e\x1c\x65\x9e\xb4\x63\x1c\x60\x96\x86\xee\xe8\x46\xe2\x80\xc6\x71\x00\x76\x9c\x17\x94\xf3\x34\x49\x4c\x88\xfd\x20\xf5\xd2\x2c\x85\x9e\x71\x9c\xa4\x01\x1d\x31\x37\x3f\xc1\x99\x97\xa5\xa0\x94\x2c\xcc\x92\x90\x45\xbe\x31\x1e\xf3\x9c\x32\x16\x40\xd8\xe4\x7e\x80\x29\xcb\xdc\xe6\x16\xa6\x49\x96\xb1\xa0\x90\x23\x43\xe4\x73\x3f\x36\x22\x8e\x28\xe1\x51\x21\x83\x55\x1e\xa5\x24\xa5\xcc\x2d\x8a\x38\xa0\x05\x25\x1c\x1c\x24\xcc\x79\x91\x12\x73\xac\x88\x29\x0b\x23\x5f\x8e\x34\x81\x8f\x63\x52\x44\x6e\xab\xa0\x41\x46\x63\x8a\x65\x26\x84\x0b\x8f\xa5\xb1\x31\x6c\xd2\x2c\x8b\x3d\x22\x95\x87\x59\x14\xf8\x09\x77\xe7\x6e\x89\x97\xf2\xa2\x28\x98\xcc\x22\x23\x1f\x73\x62\xb4\x0a\x16\x84\x5e\x94\x71\xf0\xbc\x9c\x53\x92\xe6\xdc\x9d\xbb\xa5\xbc\x48\x98\x5f\xc8\x91\x81\x64\x51\x9c\x60\x73\x5e\x11\xc5\x38\xa6\x85\x1c\xc2\xfc\x98\x84\x3e\x71\x2b\x2f\x63\x24\xf6\x79\x06\x32\xe6\x8c\x44\x11\x4e\x8c\x32\xce\x31\x8d\x52\x2a\x87\x26\x22\x0c\x89\x74\x27\x01\x87\x89\x08\xcb\x59\x9c\xe7\xe0\x20\x59\xce\x3d\x9e\x62\x63\xd8\x2c\xc2\x38\x0f\x8a\xb8\x50\x83\x2e\xcf\x71\xec\xb6\x63\x2f\x2a\xbc\x28\x96\xf9\x42\x4c\x70\x1c\x15\xa9\xd1\xa5\x3d\x16\xf9\x71\x9e\x81\x83\x30\x92\xd1\x84\x32\xf7\x08\x82\xb1\x5f\x24\xd4\x0b\xd4\xc4\x5d\xe2\xe5\xcc\xc8\x31\x4e\x63\xec\xa5\xbe\x8c\xc7\x3e\xce\x82\x18\xbb\x65\x4c\x68\x9e\xc6\x71\x11\x4a\xab\xf0\x82\x38\xa7\xc6\x78\xec\x93\x8c\xb1\x34\x06\xab\x08\xbc\x2c\x26\x41\xe2\x76\x10\x3f\x4b\x78\xca\x3d\x10\x05\x0e\xb3\x24\xe5\xa9\x51\x79\x81\x8f\xf3\x28\xce\xa0\x67\x49\x86\x3d\x2f\x0f\xdc\x76\x1c\x64\x59\x98\x07\x32\xf1\xce\x52\x9f\x07\x24\x35\x0e\x4d\x22\x5d\x21\x49\x02\xc1\xaa\xc8\xa2\x30\xe6\x22\xbc\xba\x62\x45\x91\xa5\x51\xc1\xe4\x20\xc9\xf2\xa8\x60\xdc\xc8\x71\x94\x05\x01\x4e\x28\x20\x0e\x58\x10\x87\x14\xc7\x6a\x12\xf5\xad\xe3\xd8\x6a\xfb\x5e\xf8\xea\xba\x27\x54\x6d\xd7\xa0\xbd\xea\x9c\x50\xfd\xe5\x7a\x27\x54\x43\x4c\xb6\x5b\x3a\x30\x2c\x47\xdc\x7c\xf5\xd1\xeb\x2e\x1d\x44\xcc\x4b\x78\x3d\xe1\xee\xa7\x59\x96\x78\x96\xa5\x83\x34\x8d\x62\xc6\xe5\xf0\x4b\x83\x8c\xb1\xb8\x9b\xba\x38\x88\xf8\x59\xc4\x0b\x3f\x86\x48\x56\xf0\x24\x28\xa8\x88\x64\x26\x48\x16\x06\x45\x11\xfa\xe0\x05\x61\x81\x73\x3f\x2a\xb6\x9d\xd5\x0f\xb1\xc7\x43\x22\x83\x0f\xcb\x79\x44\x49\x6e\x59\x3a\x48\x52\x2f\x8c\xa8\x34\x48\x92\xfa\x3c\xca\x70\xb1\x25\x11\x5c\x50\x3f\x4f\xa4\xcd\x17\x69\x80\xd3\x3c\xb2\xf4\x24\x4c\xb9\x97\xe5\x32\x0d\xc2\x7e\xcc\x09\x8e\x93\x5d\x96\x0e\x6e\xfa\x1c\xe9\x36\xa5\x61\x01\xce\xb3\x57\x7e\x7d\x84\xed\xa5\x5f\x1f\x11\x7b\xed\xd7\x47\xbe\xbd\xf8\xeb\xa3\xc0\x5e\xfd\xf5\x51\x68\x2f\xff\xfa\x28\xb2\xd7\x7f\x7d\x14\x5b\x0a\xc0\xca\x0e\x42\x79\x58\xe3\x3e\x70\xf9\x7c\x2e\x9f\x0f\x0f\x7b\x48\x19\x40\x73\xe3\x11\x28\xf9\x7c\x2e\x9f\x5b\x9a\x13\x68\x4e\xac\xcd\xc9\x5c\x3e\xb7\x34\xf7\xa1\xb9\x6f\x6d\xee\xcf\xe5\x73\x4b\xf3\x00\x9a\x07\xd6\xe6\xc1\x5c\x3e\xb7\x34\x0f\xa1\x79\x68\x6d\x1e\xce\xe5\x73\x4b\xf3\x08\x9a\x47\xd6\xe6\xd1\x5c\x3e\xb7\x34\x8f\xa1\x79\x6c\x6d\x1e\xcf\xe5\x73\xc3\xb6\xbe\x2d\x8b\x1e\x4b\xcb\x30\x21\x67\xd2\x28\xfa\x15\xf7\x60\xcb\xad\x34\x08\x53\xab\x54\xda\x82\xa9\x55\x26\xed\xc0\xd4\x2a\x93\x26\x60\x6a\x95\x4b\xf5\x9b\x5a\xe5\x52\xf3\xa6\x56\x5c\x6a\xdd\xd4\x8a\x4b\x85\x9b\x5a\x15\x52\xd9\xa6\x56\x85\xd4\xb3\xa9\xd5\xa9\xd4\xb1\xa9\xd5\xa9\x54\xaf\xa9\xd5\x4c\xaa\xd6\xd4\x6a\x26\xb5\x3a\x37\xd5\x1d\x74\x1d\xdd\xdd\xf2\x3a\x54\x6b\x3d\xed\x9a\xfe\xab\x52\xd6\x1e\xb6\x1d\x37\x7f\x00\x23\x78\xbd\x7c\x36\x04\xd9\xa2\x50\xb4\x24\x23\x44\xf0\xaa\xac\x4f\x1b\xe8\x55\xa3\xd1\xd7\x88\xbc\x05\x48\x73\x2d\xd7\x16\xc7\x5c\xe2\x50\xe7\x0b\xfa\x38\xe0\xd4\xfc\xb5\x2a\x50\x1f\x1d\xa1\xff\x80\x6a\xc4\x76\xe2\x75\x49\xe7\x9d\x2a\x54\x6f\x66\x4d\x9d\xe3\xcd\xd8\x59\x3c\x05\x36\xd7\x5a\xb8\xcf\xe3\x49\xa8\x59\xa7\x0a\xf6\x4c\x16\xff\xd5\x8b\x57\xcf\xa1\x44\x71\x5d\x0e\xb8\x03\x47\x07\x70\xb0\xe9\xf5\x1d\xea\x82\xc5\xae\x13\xa6\x12\x72\xde\xe1\x62\x3e\xe4\x62\x66\xe2\x62\x3e\xe4\x62\xa6\x73\xd1\x85\x8b\x87\x70\x96\x4a\xc6\xba\x4a\x2d\x35\x73\x3e\x68\xb5\xb7\x77\x29\xbe\xdd\x6a\x14\x6f\xa7\x51\xdc\x6a\x14\x6f\xa5\x51\x3c\xeb\x14\xf8\x9e\xd5\x55\xb8\xb5\xc2\xdc\x73\x55\xab\x5b\x13\x12\x56\x12\xee\x82\xc1\x3e\xe6\x44\x53\x69\x8d\x2f\x1a\x55\x29\x9e\x77\xd8\x98\x1b\xd8\x98\x99\xd8\x98\x0f\xd8\x98\x75\xd8\xe8\x22\x8c\x06\xf8\x48\xe4\xd4\xe9\x4e\xb5\xc3\x5d\xa1\x24\x6e\xd5\x1e\xbb\xd4\xfe\xaa\x8c\x65\xe4\x32\x0e\xcc\x3d\xc8\xb9\x82\x74\x9c\x09\x97\x90\x38\xd2\x02\x89\xf5\x56\xe8\x1a\x56\x32\x80\x8d\x99\x45\x1f\x76\x5e\xc3\x8e\xf2\xd0\x46\x9a\xb9\x10\x5a\x19\xf7\x47\xae\x2e\x78\x1b\xca\x66\x12\x7c\x06\x35\xdb\x04\x1e\xa1\x49\x6f\x0f\xdd\xaf\xbd\xb3\xf9\xe5\xff\x47\x18\xdd\x45\x83\x6d\xd3\x43\x3e\xc4\xbf\xb5\x06\xc7\xd9\x10\xff\xee\x37\xde\x62\xe1\x02\x5f\x97\x0b\x90\xe2\x96\x3c\x48\xed\x0c\x39\x90\x9a\x18\xd0\x37\x23\x6d\x47\xc5\x57\xa5\x4d\xbd\xed\xa8\xf7\xaa\x34\x31\x67\xaf\x89\xaf\x8a\xe2\xcf\xd0\x6d\x54\xcc\x54\x59\x7c\xf1\xc5\x7c\x8e\x4f\xb6\x91\xbe\xcf\xe7\xa2\xcd\x5c\xb5\x11\x5f\x4e\xe7\x8e\x62\xfa\x33\xa8\xa6\x2f\x50\xa7\x92\x0e\x7c\xce\xe4\xe7\x54\x7d\xb6\x37\x9f\x43\x73\x41\x25\x95\x24\xe1\x73\x26\x3f\xa7\xea\xb3\xbb\x24\xff\x4c\xd6\xe4\x57\x01\x47\x8e\x2b\x6c\x2e\xcb\x4b\xef\xc9\xe2\x07\x6c\x56\x57\xec\x57\x0f\x3b\x35\xfb\x67\xda\x2d\x12\xac\x1e\x75\x9c\x95\xf9\xe1\x6d\x6a\xd2\x20\x52\x34\x67\x5d\x9a\xf3\x0e\xcd\x59\x97\xe6\x5c\xa7\x39\xdb\x86\x26\x96\xfd\xe4\x6a\x68\x90\xe7\x4d\xb8\x1c\x14\x68\x5d\xf6\x7f\x56\x5f\x5a\xa1\x3d\x0c\xda\x87\x82\xa6\x5f\x3f\x93\x65\xb8\xdd\x34\x65\x3f\x15\x70\x4d\x73\xd6\xa5\x39\xef\xd0\x9c\x75\x69\xce\x75\x9a\xb3\x96\xa6\x31\xeb\x1c\xbf\x87\xc0\xcc\xeb\x8f\x50\x7d\xe9\x47\xfb\x61\xaa\x1f\xc1\x79\x7f\x2c\x5d\xc7\xa8\x7e\x84\x60\xf0\x63\x69\x0b\xa1\x1f\xe0\xa2\x04\x01\x33\x9b\x37\x2c\x9a\x9c\x52\x02\x0a\x82\xb3\xb6\x2f\x32\x5c\x54\x58\x0f\x17\xb3\x6d\x62\x55\x4b\x56\xfc\x2b\x24\xe2\xa6\x59\x01\xa9\x6c\x66\x22\x98\x5d\x8b\xe2\x8f\xc6\xd0\xd3\xa7\xf8\x63\x69\xa2\xf8\x63\x79\x1d\x8a\xe6\x60\xd7\xa7\xf8\xca\x48\xf1\x95\x89\xa2\xd9\xda\xfa\x97\x57\x58\x48\xc2\xe4\x45\xed\xf6\x00\x68\xe5\x0e\xe6\x41\xea\xa8\xb4\x2f\xc3\x23\xb0\x48\x74\x16\x6b\x5c\xdb\xb1\xf9\xf3\x79\xce\x2a\x8e\x2e\xdd\x6f\xfa\xe2\x0f\xde\x37\x8d\xf6\x0d\xaf\x9b\xa7\x26\xb6\x61\x00\x2a\x4c\x6d\xe0\xc5\xb6\x30\xb5\x81\x77\x68\x6e\x6a\x03\xaf\xd0\xdc\xd4\x06\x5e\xc9\x27\xf9\x1c\xae\xef\x98\xdb\xee\xef\x80\x77\xfa\x49\x3e\x03\x28\x29\x3a\xae\x4b\x2e\x1f\x08\xcd\x7a\x13\x88\xc0\x94\x99\x78\x84\x29\x85\xcc\xc4\x23\xcc\x5e\xa4\xa6\x36\x30\x79\x91\x9a\xda\xc0\x3c\x09\x33\xb5\x81\x69\x92\xc1\x6d\x06\xe2\x0f\xa6\x5d\x26\xd2\xd4\x2b\x62\x15\x06\x4c\xdc\x4c\xa4\x1c\x84\x65\xed\xb7\x23\x8e\x94\x46\x35\x4c\x76\x6e\xf4\xb2\x12\x6d\xce\x10\x32\x83\x47\x60\xff\x6c\x90\x0d\x3c\x6a\x8a\x51\x4c\x1e\x81\xdd\x33\xc9\xec\x23\x4f\xe7\x96\x0d\x99\xed\xe3\xd1\x66\x19\x25\x41\x10\x51\x3a\x24\x88\x5b\x82\x20\x9e\x54\x11\xec\x44\x82\x74\x9c\xa0\x36\x2f\x29\x09\x12\x08\xb1\x43\x82\xa4\x25\x48\x66\xf5\xb8\x34\x01\x78\x2d\xbc\x8e\x13\xd4\x66\x32\x25\x41\x5f\x10\xcc\x87\x04\xfd\x96\xa0\x2f\x68\xe5\x8a\xa0\x3f\xe2\x0e\x7d\x3c\xda\xdc\xa7\x24\x18\x08\x82\x7c\x48\x30\x68\x09\x06\x82\x16\x57\x04\x03\x9d\x20\x1f\x27\xa8\xcd\x96\x4a\x82\xa1\x20\x58\x0c\x09\x86\x2d\xc1\x50\xd0\x2a\x14\xc1\x50\x27\x58\x8c\x13\xd4\xe6\x57\x25\xc1\x08\x5e\x2a\x86\x04\xa3\x96\x20\x64\xef\xa7\x8a\x60\xd4\x79\x89\x18\x27\xa8\xcd\xc8\x4a\x82\xb1\x20\x38\x1b\x12\x8c\x5b\x82\xf0\xda\xa4\xc6\x64\x01\xef\x4a\x02\x3e\xf9\xec\xc5\x97\x4b\x71\x6e\xee\x52\x1c\x2c\x92\x7b\x75\xb3\x99\x40\x06\x75\x58\x7c\xef\xa6\xaf\xc5\x31\x93\xc1\xff\x94\x17\xe3\x9c\x2c\x17\x1f\xf8\x4a\x56\xf9\x45\xd5\x12\xf9\xe4\x20\x2d\x2b\x91\xa0\xe4\x88\xc1\xfe\xec\x94\x17\xcb\x15\x57\xdb\xa9\x07\x5a\xd3\xce\x9a\x68\x6b\x77\xd5\xf2\xb5\x4f\x6e\xe2\x22\x9e\x3f\xea\x15\x3c\x3a\x9f\x4d\x7d\x90\xbb\x08\x7b\x24\x38\xf2\x55\x9d\xe2\x2f\xa7\x9b\xac\x47\x95\x42\x4c\x76\x3d\xdd\x24\x9a\x8c\x9c\x6e\xea\x6c\x6b\x18\x9c\x6e\x0a\x31\xf9\x72\xba\xe9\xa6\x4f\x37\x09\xad\x6c\x77\xba\xc9\xa8\x9c\xce\xe9\x26\xa9\x20\xe7\xe9\x26\x79\x8e\x76\xcb\xd3\xdf\xfe\x1f\xfa\x3c\x13\x5f\x64\x07\x29\x5b\xf3\x28\xe8\x3d\x38\xcb\xc3\x3e\xe8\x87\xf3\xf7\x79\xd1\xfb\x31\x2b\xcf\x67\x7c\xf5\xbb\x1c\x89\xd2\x58\x85\xef\x82\x43\xf9\x40\x32\x06\x9f\x75\x7e\xfe\x15\x8e\x4e\xbd\xda\xea\x4e\x20\xd8\x3c\x73\x02\x5d\x6f\xe0\xb4\xdf\xc6\x8f\x42\x1d\x1d\xa1\x67\x7c\x75\x06\xa3\xe8\xc9\x6c\x59\x66\x1c\xe1\xfe\xb5\x29\xa2\xf9\xb3\x13\xdc\x3d\xbb\x14\xc6\x53\x14\x24\x53\x14\xe0\x29\xf2\xfd\x29\x22\xe1\x14\xe1\x78\x8a\x92\x29\x42\x58\xdb\x6a\x14\xd2\x29\x0a\xbd\x29\x0a\xc8\x14\xf9\xc1\x14\x91\x68\x8a\x30\x9d\x22\xec\x4d\x11\xd1\xe1\x92\x29\x0a\xf1\x14\x05\xfe\x14\xf9\xe1\x14\x91\x78\x8a\x70\x32\x45\x58\xe0\xd7\xe0\x22\x6f\x8a\x42\x32\x45\x41\x30\x45\x7e\x34\x45\x91\x3f\x45\x61\x38\x45\x41\x3c\x45\x7e\xa2\x01\xfa\x78\x8a\x88\x3f\x45\x38\x9c\xa2\x78\x8a\x50\x44\xa6\x28\x0c\xa6\x28\x80\xab\x05\x74\x40\xc1\x09\x99\x22\x1c\x4c\x51\x24\x00\xf1\x14\x85\xfe\x14\x05\xe1\x14\xf9\xb1\x06\x48\x92\x29\x22\x78\x8a\xb0\x20\x39\x45\x88\xd0\x29\x22\xde\x14\x61\xc1\x8e\x04\x7b\xeb\x90\x2b\x31\xcb\x95\x74\xe5\x2a\xb8\x10\x72\x14\xfd\x26\xe2\xf3\x14\xa1\x50\xe7\x56\x11\x16\xdd\x12\xdc\x02\x43\x9e\xce\xa5\xaf\x04\x27\xb8\x12\x00\xd1\x14\xe9\xdd\xc5\x91\x94\x87\x10\x30\x70\xef\x77\x15\x21\x14\x2a\x04\x2c\xe4\xe7\xc7\x52\xb0\x61\xd8\x93\x57\xe0\x29\x6d\x85\x52\xfb\x81\x4e\x41\xa8\x46\x98\x86\x2f\x54\x1a\x49\xb5\x87\xba\x0e\x85\x0a\x84\x3d\x08\xbb\x10\x3a\x14\x82\xad\xb3\x9a\xce\x8d\x50\x17\x67\x17\x73\x06\xd7\xa4\x88\xa4\x72\x3d\x2b\x8b\xc1\x0d\x4f\xe0\x05\x3f\xbc\xfc\xe5\xc5\xa3\x1f\x1e\xca\x3b\xa5\x84\xc4\xc8\x14\x41\xe7\x85\x84\xa8\xb0\x48\xa5\x26\x90\xae\xb2\x54\xac\xd4\x49\x94\xf5\x82\x40\xa8\x4e\xff\xc5\x77\x4f\x5f\xf3\x35\x62\x8b\x5c\xd5\x46\x3f\x07\x95\xca\xfb\x34\x0c\x7c\x08\xf8\x5f\x9e\x75\xf5\xd9\x4b\x29\xbd\x8d\x77\x17\x5e\x46\x28\xf1\xbc\x69\xff\x59\xfd\xae\x20\x41\x0c\x00\xa4\x03\x40\x3d\x8f\x0c\x40\x7c\x0d\x64\xf8\x34\xd0\x9f\x1a\x08\x84\x5d\x02\xc4\x40\x20\xea\x32\x69\x02\x89\x7b\xfd\x30\x10\xa2\x1d\x46\x86\x28\x92\x3e\x95\x21\x0a\xa6\x83\x98\x00\xd2\xbe\xb4\x86\x20\x59\x8f\xcc\x00\x20\xef\x77\x65\x08\xc2\x35\x90\x21\x85\xa2\xcb\xe5\xb0\x39\x75\xb5\xc6\x74\x54\x1f\x84\x8e\x10\xf0\xe9\x88\x55\x05\x7d\x22\x06\xbb\xa0\x6e\xbb\x89\xe8\xa8\x61\xc6\xd4\x65\x98\x94\x8e\xea\x3b\xa1\x23\xfa\x66\x7d\x26\x0c\x26\xd1\x27\x33\xe4\x24\xa3\xa3\x1a\xcf\xe9\x88\xd5\x70\xea\xb6\xee\xa2\x4f\xc3\xa0\x79\xab\xba\x54\x94\xc0\x66\x41\x12\xed\xa9\x45\x99\x7e\x07\xc4\x48\x3d\xe8\x62\x31\xf5\x31\xd4\x41\x8c\x36\xa1\xf3\x69\x78\x1e\x77\xd9\x70\xf8\x06\x76\x98\x7f\xd2\xe7\xd4\x1a\x28\xb0\x43\xa3\x69\xb7\x33\x06\xab\xe8\x74\xc6\x1a\x27\xb0\xc3\x7e\x79\x0f\xc4\x16\x2a\xb0\x39\x14\xd0\x51\x51\x60\x3a\x2a\x0a\x42\x47\x55\xef\x53\xb7\xda\x82\x1e\x0a\x5b\xac\x70\x89\x3b\xa2\x2e\x13\x8e\xe9\x88\x32\x28\x1d\x91\x64\x42\x47\x4d\x8b\x51\xb7\x42\xd3\xbe\xbc\x0d\x83\x47\x9f\xca\x10\x24\xa7\x2e\x95\x72\x3a\xe2\x42\x45\x5f\xa3\xfa\x1d\x55\xd3\xb1\x2c\x23\xf0\x3c\x1a\x78\xd8\x1a\x41\x14\x8c\x35\xcd\x68\x14\x68\x8b\x20\x35\x11\xcf\x44\x24\xe8\x12\x31\xc2\x84\x5d\x3c\x46\x66\xa2\x2e\x1e\x23\x4c\xdc\xc2\x18\xa8\xe8\xc1\xd6\xd8\x3c\xe9\x93\x30\x20\x61\xfd\xee\xd8\x13\x0e\x45\xc8\x80\x24\xeb\x08\xd6\x00\x90\xb7\x00\xd6\x00\x22\x59\x30\x34\x2e\xfa\x5a\xb1\xe6\x5d\x4e\x61\x62\x3a\xd2\x0b\x42\x5d\xd2\xf6\xfb\x24\x4c\xb6\x41\x7b\x7a\x37\xd9\x06\x1d\x17\x78\x44\x47\x0c\x35\xa6\xe3\x86\x4a\xe9\x88\x52\x12\xea\x50\x0a\xa3\x6e\x5f\x4a\xfb\x1c\xd8\x03\x89\xd3\x55\x72\x3a\x62\xc4\xbc\x2f\x53\x7b\x3c\xb1\x5a\x90\xfe\x02\x62\x78\x8a\xb7\x70\x7b\x4c\xb6\x70\x26\xec\x6f\xe1\xf8\x38\xd8\xc2\x9e\x71\xe8\x74\x7d\x1c\x8d\xb9\x24\x8e\x47\x82\xa1\x9e\x82\x9b\x31\x24\x63\xe1\x12\xb3\x31\xbf\xc7\xe9\x16\xd1\x12\x67\x63\x81\x0c\xe7\x5b\x04\x4b\xcc\xb7\x08\x65\xb8\xe8\x6b\xc8\x68\x2e\x63\xa1\x02\xe3\x31\x0f\xc5\x64\x0b\x07\xc1\xfe\x88\x97\xe1\x60\x9b\xc0\x16\x6e\x11\x76\x70\xe4\x8c\x6e\x38\xde\x22\x2c\x61\xba\x85\x2f\xe2\x64\x0b\xaf\xc7\x6c\x8b\x68\x8a\xd3\xb1\x08\x86\x33\x57\x08\xc3\xf9\x58\x58\xe0\x5b\x84\x51\x5c\xf4\x22\xd4\x2e\xa9\x0a\xf6\x02\x4b\x30\x32\xb3\x4c\x3a\x52\xc1\xd6\x14\x45\xe2\x36\x61\x0f\xb4\xe7\x9e\xe1\x79\xd8\x53\xce\x10\x22\xea\x08\xcd\x44\x23\xee\x40\x8c\x0f\xc7\xf6\xdc\xa4\xa5\x62\xcb\x4c\xea\x9e\xda\xb2\x92\x96\x8b\x21\x9f\x59\x4f\x9a\x43\x88\xbc\x23\x2d\x5b\x6a\x02\x18\x2c\x69\x89\x6a\x6b\x96\x80\xab\x7b\x98\x8e\xb1\x4f\xa8\xdd\x50\x7c\x3a\x66\x28\x01\x1d\x53\x74\x48\xdd\x9d\x8f\xa8\xdb\x94\x62\xed\xf9\xf0\x29\xa5\x76\xd1\x25\xd4\x25\x3a\x46\xc7\xcc\x2b\xa5\x6e\x27\xc8\xa8\xdb\x74\x72\x3a\x66\x18\x9c\x8e\x39\x41\x41\xc7\x4c\xbc\x93\x56\x58\x8c\x00\x8f\xb8\x2b\x26\x23\x16\x8a\xfd\xd1\x90\x81\x03\xa7\xa5\xe2\x70\xd4\xe1\x71\x34\x1a\x35\x70\xec\x8a\xc4\x74\xd4\x13\x71\x32\x1a\x32\x30\x73\x78\x23\x4e\x47\xc2\x05\xce\x46\xa3\x16\xd6\xc3\x81\x81\x04\x1f\x89\xbd\xb8\x18\x0d\x49\x2a\xb5\x70\x76\x13\x3b\xfd\x0a\x93\xf1\xd0\xe2\x3b\x22\x07\x0e\x46\xdc\x1a\x87\xa3\xb1\x05\x47\x4e\x07\xc6\xf1\x68\x6c\xc3\x74\x24\xf8\xe0\x64\xd4\x03\x31\x1b\x09\x03\x38\x1d\x8d\x81\x38\x1b\x0d\x05\x38\x1f\x8d\x47\x98\x3b\x82\x1d\x2e\xba\xd1\x68\x97\xfc\x81\x7a\x92\xa4\x39\xb6\xd4\xd9\x27\xf6\x02\x4b\x2a\x51\x33\x6d\x78\xee\xb7\x18\x02\xb3\x21\x06\x76\x23\x0a\xbb\x12\x31\xe7\x10\x4d\x72\x6c\x22\x1f\x7b\x9d\xf4\xcf\x3e\x7e\xd6\x2b\x2a\xe6\x0c\xa2\xd5\xad\x39\x7f\x90\xcf\xcd\xb9\x43\x2b\x3e\xdb\x0a\x4a\x2b\x1e\x03\x8e\x5c\xf3\x52\x4b\xe6\x50\x9b\xb7\x39\x77\x68\x15\x6c\xe9\xbf\x53\xbf\x98\xda\xbb\x47\xe8\x18\xf3\x3e\x1d\x13\x40\x40\xdd\x2a\x0e\xe9\x58\x17\x22\x6a\xb5\x9f\x98\x8e\x19\x1f\xa5\x2e\xf9\x25\x5d\xe2\xb6\x24\xc2\x61\x1d\x29\x75\x69\x2f\xa3\x63\xd6\x97\x53\xb7\xfd\x72\xea\x76\xbf\x82\x8e\x79\x08\xf6\x46\x5c\x04\xe3\x11\x2f\xc4\x64\xd4\x0d\xb1\xef\x1a\x29\x9c\x16\x8e\xc3\x51\x17\xc1\x91\x37\xa6\x27\x1c\x8f\x46\x32\x4c\x47\xbd\x05\x27\xa3\xe1\x02\xb3\xd1\x80\x87\xd3\x91\x98\x89\xb3\xd1\xb8\x81\xf3\x91\xb0\x84\xb9\x23\x2e\xe1\xc2\x19\x36\x64\xf6\xe0\xee\x03\x1e\xf5\x4b\x4c\xec\x8e\x89\xfd\x11\xb7\xc7\xc1\x88\xe1\xe3\x70\xd4\x77\x70\x34\x1e\xdd\x62\x47\x78\xc3\x74\xdc\x79\x12\x67\xfc\xc0\x6c\x34\xfe\xe1\x74\x34\x88\xe2\xcc\x19\x44\x70\x3e\x1a\xa5\x30\x1f\x09\x53\xb8\xe8\xc6\x91\xdd\x92\x07\x63\x4c\xa9\xf9\xb5\xad\x90\x34\xdc\x18\x53\x86\xbb\xda\x76\x0d\x63\xc6\xa0\x00\x60\x3e\xc5\x98\x37\x34\x39\x9f\xe1\x79\x54\x23\xb0\x01\xc4\x2d\x83\x86\xa7\xba\xce\x6d\x29\x43\xcb\x9f\x25\x67\x68\x7b\x68\xa0\x90\xb6\x0c\x9a\x59\xc8\x3a\x00\xa6\x81\xc3\xea\x7b\x5c\x57\x8e\x01\x75\xd1\x11\x8e\x79\xce\xc1\xd5\x1e\xd3\x11\xe1\x12\xea\xd9\x0c\xc7\xa7\x6e\xc3\x09\xa8\xcb\x70\x42\x3a\x62\x17\x11\x1d\x91\x5a\x4c\x47\x4c\x8f\xd2\x11\xd5\x26\xd4\x26\x77\x46\x47\x74\x9a\x52\xb7\xd5\x66\x74\xc4\x6a\x72\x3a\xa2\x39\x4e\xdd\x86\x5b\x50\x97\xd9\x63\xcf\xe9\xb6\x18\x7b\x56\xbd\x62\x32\xe6\xd3\xd8\x1f\xf3\x49\x1c\x8c\x78\x35\x0e\xc7\x9c\x02\x47\x63\x91\x03\xc7\x23\xbe\xdd\x8c\x7b\x56\x35\xe2\x64\xcc\x81\x30\x1b\x89\x8f\x38\x1d\x8b\x20\x38\x73\x46\x28\x9c\x8f\x45\x18\xcc\xed\x83\x73\x31\x12\x21\x20\x3f\x70\xeb\x0a\x8f\x58\x1a\x26\x23\x9e\x8e\xfd\x31\x67\xc6\xc1\x98\xb3\xe2\x70\x2c\x54\x45\xf6\x50\x84\xe3\xb1\x60\x81\xa9\xdb\x5d\x92\x31\x87\xc7\xcc\x1a\x2c\x70\x3a\xe6\xcb\x38\x1b\x09\x17\x38\x77\x06\x4b\xcc\xc7\x42\x19\x2e\x7a\x01\x67\x97\xac\x40\xb1\x4d\x4d\x51\xa4\xc6\x69\xca\x0b\x64\x5b\x62\xee\xb3\xdf\x3e\x27\x26\xdc\x41\x2b\x11\x23\xfe\x50\xef\x8f\x29\x2b\x68\x9e\x0e\x71\xc7\x1d\x83\xb6\x8e\x8a\xc6\x6c\x40\x63\x6a\x88\x98\xd5\x64\x8d\x2c\xa7\xca\x40\x4d\x19\x80\x26\xab\xe1\xf3\x5c\x43\x3b\x7c\xca\x9b\xbe\x0e\x9f\x15\x1d\x29\x9b\x7a\xea\x54\x12\xa6\x6e\x25\x11\x6a\xe9\x91\x4f\x5d\xda\x09\xa8\xab\x3f\x21\x75\x5b\x5d\x44\xdd\x96\x11\x53\xbb\x3c\x28\x75\xd9\x45\x42\xed\xf6\xcc\xa8\x5b\xf5\x29\x75\xeb\x30\xa3\x16\x9b\xca\xa9\x5b\x45\x9c\xba\x6c\xaa\xa0\x6e\x53\xc6\xde\x88\x1f\x61\x3c\x62\x7c\x98\x8c\x78\x2a\xf6\x1d\x06\x88\x03\xa7\x9f\xe2\x70\xc4\x15\x71\xe4\x8d\xc4\xa0\xd8\xe9\x73\x4d\x06\x6b\xe1\x3d\xb1\x46\x6d\x66\xf3\x56\x9c\x8e\x84\x36\x9c\x39\xe2\x22\xce\x47\x62\x08\xe6\x23\x3e\x8b\x0b\x67\x70\x13\x23\xba\x85\x71\xec\x34\x25\x4c\x9c\x4e\x8b\xfd\x11\xbf\xc4\xc1\x88\x63\xe2\xd0\xe1\x99\x38\x1a\x89\x35\x38\x1e\x0d\x56\x23\x9e\x84\x93\x11\x1f\xc5\xcc\x11\x00\x70\xea\x8c\x5a\x38\x73\x86\x16\x9c\xdb\xfc\x1f\xf3\x31\x17\x2e\xba\xa1\x67\xf7\xa1\xdb\x60\x23\x35\xab\x81\x87\x0d\x43\xb7\x4a\x35\x0c\x83\xb6\x42\x6a\x6a\x16\x34\x49\x8e\xe9\x69\x68\xe9\x7e\x24\x51\x1a\xc6\xe8\x36\x65\x1a\x3e\xa5\x5a\x07\x4c\xc3\x74\xd3\xf7\x61\x53\xa6\x19\xf9\xf0\x69\xaa\x75\xc2\xf4\xaa\xae\xe5\x71\x86\x61\x5a\xca\x6d\x88\x95\xb7\x72\x33\xbd\xa4\x6b\x99\xef\xb0\xa7\x2e\x31\x60\x6a\x16\x2a\xa1\x2e\xfd\xfa\xd4\xd5\xc7\x80\x3a\x0c\x27\xa4\x2e\xe1\x45\xd4\xd5\x93\x98\xda\xc4\x43\xa9\xc3\xac\x12\xea\x52\x35\xa3\x2e\x8d\xa4\xd4\x61\x08\x19\xb5\x99\x79\x4e\x5d\x96\xcc\xa9\xd9\x62\x0b\xea\x50\x32\xf6\x9c\x5a\xc6\xd8\xe9\xae\xc4\xe9\xaf\xd8\x77\xfa\x0a\x0e\x5c\xee\x80\x43\xa7\x2b\xe1\xc8\xe9\x10\x38\x76\x45\x04\x35\xde\x18\x1f\x25\xce\x68\x81\x99\xcb\x63\x70\x6a\x09\x1a\x38\xb3\x05\xd9\xdc\xe9\xb9\x98\x3b\x83\x02\x2e\xac\x11\x11\x7b\x4e\xad\x63\xa7\x23\x62\xe2\xf6\x6e\xdf\x62\x69\x38\x70\x3a\x1a\x0e\x5d\x2e\x8c\x23\xab\x1f\xe2\xd8\x19\x19\x30\x75\x7a\x3f\x4e\x9c\xbe\x88\x99\x25\x58\xe1\xd4\xe9\x6e\x38\x73\x45\x07\x9c\x5b\xbd\x18\x73\x67\xe4\xc0\x85\x16\x1c\x76\x19\x53\xa9\x18\xe0\x89\x01\x61\x23\x9c\x61\x3c\xbe\xdb\x2e\x6e\x0c\xc3\xb1\x6c\x37\x0c\xc4\x0a\x9f\xe1\x51\x28\xf1\x11\x23\x1f\x51\xf3\xd0\x14\x84\x15\x27\xe6\x71\x86\x7a\x66\xfe\x93\xa6\xdf\xa6\x10\x2c\xf9\x34\x3d\x4a\x1b\xa4\x06\x3e\xb3\xbb\xf2\xb0\xc7\x30\xfc\x9a\xed\x84\x37\x42\x34\xb4\x29\x14\x13\x86\x47\xf5\xa2\x92\xb5\xe7\xf2\x31\x76\xc9\x54\xc1\x10\x97\xfe\x15\x8c\xef\xd2\xb5\xfa\x3d\x70\x09\x5b\xc1\x84\x76\xb1\x2a\x88\x68\xb4\xcf\xb1\xc5\xb4\xd4\x63\xea\x92\xa8\x82\x49\x6c\x5a\x52\xcf\x99\xdd\x4a\x15\x44\xea\xb2\x47\x05\x93\x99\x55\xae\x9e\xe6\x2e\x33\x52\x30\xdc\x65\xa2\x0a\xa6\xb0\x7b\x68\x9d\x11\x1b\x1d\x1b\xbb\x7a\x80\x89\x45\xc8\xd8\xb7\x59\x1c\x0e\x5c\xcc\xe2\xd0\xa5\x16\x1c\xb9\x84\x81\x63\x47\x17\x6d\xf1\x37\xb1\xab\x10\x33\x97\xa5\xe2\xd4\x19\x0f\x33\x97\x47\xe1\xdc\x6e\xdf\x98\xdb\x8c\x0e\x17\xe3\xde\xd5\xbe\xdc\x58\x21\xb0\x3b\x16\x60\x32\x6e\x70\xd8\x1f\xf3\x3e\x1c\x38\xbd\x0f\x87\xe3\x41\xa0\x56\xb6\xb3\xbb\xf1\x78\x50\xc2\x74\x3c\xb8\xe1\x64\x3c\x1a\xd4\xe6\xe0\xf2\x32\x69\x14\xd6\xa7\xd9\x58\x58\x93\x86\xe1\xe0\x93\x8f\x45\x9c\xda\x48\x80\x8a\x36\xb2\xcb\x8f\x7a\x5d\x83\x27\x6c\xfd\x7e\x8d\xaa\x19\xab\xd0\x9a\xcf\x79\x56\x41\x3d\xa2\x17\xdf\x3d\x7d\x8d\xca\xc5\x79\x7d\x4d\x44\x53\xd1\xe0\xc9\xb7\x2f\x7a\x17\x17\xb7\x07\x13\xa7\xa8\xdd\xf8\x0f\x17\x28\xaa\x2f\xf0\x59\x7d\x99\xea\x0d\x3d\xf5\xab\x04\x90\x5f\xea\xcf\xe2\xcb\x54\xeb\x4f\x9f\x73\xad\xaa\xd2\xf7\x0f\x5e\xc8\xc2\x58\x48\x16\x7e\x71\xdf\x51\x25\xa0\x9b\x0b\xaa\xe4\x17\xad\x4a\xca\x75\xaf\xa8\x72\x97\xd6\x7b\xcf\xaf\x9a\x12\x60\xef\xf9\x95\xa1\xf4\xdd\x7b\x7e\x55\xd7\xd5\x7b\xcf\xaf\xcc\x65\xf5\x04\x0d\xa9\xa2\x30\x42\x69\x59\xad\x11\xcb\xb2\xe5\x2a\x2f\x17\xa7\xa8\x5a\xa2\x67\x27\xd8\x88\xf7\xbb\x12\x4a\x01\xbd\xe9\xd7\x40\x36\xdd\x1d\x12\x46\xf6\xbb\x43\x5a\x74\xcf\x96\x02\xe1\xb3\x13\xfc\xa6\x7c\x8b\x0e\x10\x36\xd4\x28\x55\x74\x65\x79\xfe\x49\xdd\xbb\x37\x6d\x7b\x55\x8e\x4f\xfc\x67\xe2\x63\x74\xa0\xa1\x86\x3a\x7c\x7b\xe8\xf6\x00\xb1\xa1\x60\xe9\xb7\xeb\x35\x3f\x4b\xe7\x1c\xe1\x08\xad\x2f\xd2\xf7\xfc\xca\x20\xfe\xf5\x45\xfa\x23\xbf\x5a\x37\x2a\x68\xbf\xdb\x85\xb2\x78\x01\x40\x52\x34\xf5\x97\xfb\x08\x47\xcd\x37\xfb\x15\x2b\x27\x50\x71\x4a\xf1\x63\x16\xe4\xba\xc6\xae\x78\x79\xa3\x90\xbe\x55\x4c\x19\xf1\xba\xaf\x6e\x49\xcb\xea\x05\x54\x45\x39\xd6\x8a\xa0\x34\x78\x6d\x28\xa5\x41\x05\xd4\x68\x50\x64\xd8\xc6\x64\x35\x24\xb0\x5b\x4d\x97\x4e\xb1\x5a\x9e\x41\x80\x99\xf3\xa2\x42\x84\x82\x67\x08\xca\xe6\x86\x52\x38\x6f\x26\x25\x3a\x92\x77\x43\x78\x50\xc0\xb1\x36\xae\xc9\xe4\xd9\x09\x51\x36\xb8\x87\xf6\x1b\x09\xec\xa1\xbf\x20\x42\xdf\x42\x8d\x47\xb0\xad\x12\xfd\x05\xee\xb8\xd8\x9a\xbd\x55\x79\x3a\xdb\x9e\xbf\x00\xca\x77\xb6\x4c\xee\x75\xb8\x24\x14\x1e\x4b\x5e\xd1\x3e\x22\x81\x85\xe1\x3d\x03\xc7\x03\xb2\xa6\xca\xfe\xa2\x03\xe5\x22\xe3\x88\xb3\x6c\xa6\xcc\x0e\x95\x6b\xc4\xce\xcf\xe7\x25\xcf\x85\x2e\xd9\x02\xf1\xcd\x39\x5b\xe4\x3c\xaf\xeb\x32\x42\x78\x9f\x1a\xb1\x09\x11\x28\x34\x19\x5b\xa0\x94\xa3\x74\xb5\x7c\xcf\x17\xa8\x5c\x54\x4b\x44\x65\x51\xe0\x35\x5a\x67\x6c\x2e\xd1\x4b\x94\x6b\x33\xb6\xcb\x59\x99\xcd\x10\x9b\xcf\x97\x97\x6b\x40\x2d\xf0\x56\x4b\x81\xf6\x62\xcd\x73\x74\x59\x56\xb3\xe5\x45\x25\x19\x5c\x97\xcb\xc5\x10\x8b\x12\x34\x94\xd7\x9c\xb4\x5f\xee\xdf\x57\xd7\xca\xb4\x3f\x89\x80\xe2\x63\x93\xe4\x3a\x96\x8b\xa5\xe5\xc6\x6e\xc3\x55\x68\x21\x88\xb5\x9f\x21\x66\x4d\x4a\xa9\xc4\xaf\x91\xd0\xbe\x6f\x56\x95\xad\x1f\xb1\xde\x8f\xf8\xad\x2a\xec\xf9\x9b\xfe\x13\x5c\x0a\x30\xb8\x6a\xc7\x10\x01\x4f\x64\xe1\x4b\x54\x2e\x3e\xf0\xd5\x9a\xdb\xa3\x60\xb9\xf8\xf0\xa2\x17\x08\x3b\x3f\x6d\x35\x40\x60\xc7\x00\xd1\x62\xd3\x25\xb6\x7e\x83\x43\x61\xd0\x7d\xec\x1f\x3b\x13\x0e\xed\x17\xbe\xc8\x56\x57\xe7\xd5\x0e\x57\x01\xaa\x8a\xb5\xcb\x93\xa6\x5d\x0b\x3c\xed\x86\x7c\x6b\x09\xdd\x9c\x7f\x0e\xaa\xad\x44\x5c\xb5\x7b\x4f\xdc\x94\xa7\xb5\x20\x4d\x49\xc7\x7f\xf0\x4a\xcf\xd3\xba\xcc\xcd\x01\xa9\x76\x35\x56\x5f\x07\x12\x6c\xd5\x07\x83\x9b\xb3\x0c\xd9\xc7\x0f\x8b\xb2\x2a\xd9\x5c\x2f\x7d\xd5\x85\xe1\x9b\x6c\xc6\x16\xa7\xfc\xf1\xf3\xb6\x2c\xaa\xac\x3c\xe6\x6d\xbc\x42\xfe\xaf\x6f\xd2\xe6\x36\xf2\x7e\x6a\x78\x63\x2d\x0a\x6b\x9b\xe7\x8f\xf5\x36\x04\xe8\xf8\xea\x6f\xbb\x36\x54\xf2\xe6\x15\x85\xf8\xff\x96\xbc\x41\x9b\x50\xfd\x19\x2b\xd3\xba\xae\x6a\x93\xe5\xc3\xc0\xa3\xe4\x47\xe9\x55\xf0\x79\xfc\xda\x36\xc3\x48\x64\xcc\x27\x00\x9d\xed\xda\x8b\xc6\x30\x74\x3b\xb1\xc0\xae\xba\xb0\x2b\x05\x6b\x64\xf2\x21\x2f\xd7\x15\x9f\x37\x56\x6c\xc6\x58\x40\xe7\xb7\x4b\x2d\xa8\x3b\x40\x17\x62\xa0\x95\xa5\xd6\xde\x94\x6f\xdf\x4c\x26\x8a\xdb\x77\x6d\xb8\x16\x89\x64\xf3\xea\x02\xdf\xa1\xac\xb6\x49\x34\x86\x80\xdd\x73\xa4\x95\x4d\x52\x3d\x4f\x9a\xd7\x6c\x14\xe3\x01\xfc\xe7\x45\xbe\x44\xeb\x4b\x76\x2e\xd3\x8f\x39\x5b\x57\xd2\x18\x86\x21\xbc\x72\xab\xac\xc7\x6c\x57\x61\x2e\xc7\xaf\x0c\x36\x0c\x15\xc5\x77\x75\xf5\x81\x6b\xdc\x98\x0b\x5e\xc7\xd5\xaf\x13\x52\x46\x42\x97\xe1\x8d\xac\x42\xcb\x8b\x6a\x10\x81\x9b\x90\xeb\x56\x59\x27\xe4\xda\x75\xd6\x19\x32\xde\xf3\x2b\x59\x02\x3a\x0a\x8e\x7c\xa2\x3f\x29\x3f\x58\x1e\x68\x75\xa3\x23\x63\xd5\xe8\x23\xf4\x42\x58\xa0\x7a\x09\x58\x2d\xd7\xeb\x36\x4d\x87\x9a\x87\x90\x10\xc3\x6b\xa9\x6c\xd1\x0c\x54\xad\xe0\x26\xf5\x78\x75\xc6\xd6\xef\x3b\x2e\x5b\xdb\xee\x64\xd2\x31\x51\xe1\x88\xf5\xe8\xfa\xae\xd3\x75\xe1\xb4\x02\x8b\x26\x82\x8e\xc9\xbe\x03\x9b\xfd\xca\x68\xf8\xe2\x99\xc8\xa8\x24\x66\x05\x55\xfb\xdd\x80\xed\xe7\x8f\xb7\x67\x7b\x65\x67\x7b\xee\x66\x7b\xee\x60\x7b\xb5\x05\xdb\xce\x22\xd2\xeb\xba\x8a\xb4\x9c\xfe\xd8\xae\x8e\xf4\x58\x11\x66\x89\xab\xe2\x9b\x4a\x2f\xc5\xfc\xfd\x83\x17\x87\x2a\x41\xeb\xd4\x62\x9e\xa2\xac\x38\x35\x14\xd7\x3e\x9f\x33\xc1\xc4\xa6\x42\x7d\x2c\x2a\xe1\x9a\xb4\x74\x4c\x88\x9a\xca\xce\xc3\x89\x9a\x6e\xd1\xed\xef\x1f\xbc\x30\x56\xdc\x7e\xb9\x2a\xcf\xe7\xfc\x60\xb7\x29\x22\xd9\xa8\x33\x51\xa4\xff\xf4\xc7\x99\x2e\x52\x13\x11\x82\xed\x12\x2a\x94\x66\xfd\xeb\x81\x54\x16\xcb\xd7\x18\x1d\x0b\xb8\x43\x29\xd5\x07\x52\xc7\xcb\xd5\xa4\xbd\x67\x5d\x5d\x1c\x5f\x93\x3e\x5c\xcf\xcb\x8c\x4f\xbc\x29\x22\x7b\x83\xbb\x30\x1a\xb4\xe4\x9a\x68\xc9\x14\x05\x0e\xb4\xfe\x35\xd1\x06\x53\x14\xed\xd9\x2f\xd2\xb8\xf6\xbb\x07\x5f\xe3\x43\xbd\xb1\xd6\xc2\x2a\x99\x43\xfd\x9d\x63\x8b\x06\xfe\x16\x14\x6e\xe6\x9d\x46\xd0\xda\x91\x39\xb2\x6b\xf7\xf1\x16\x14\xcc\xa3\x1e\x4e\xc8\x8d\x0d\x7b\xff\x24\x61\xb5\x89\x2e\x37\x10\x5c\x5b\x5c\x3b\x86\x58\x5b\x88\xeb\x06\xda\x06\xca\x59\x3f\xbf\x81\xea\x95\xd0\xd7\x0a\xb3\xdf\x0d\xc9\xb4\x57\x55\x5f\x2b\xee\x7e\x37\x0c\xa6\x6d\x55\xf7\xbb\x61\x34\x55\xc5\xde\xef\x46\xf8\xe3\xdb\x29\x0d\x3e\xa9\xe0\xfe\xef\x59\x69\xff\xb3\xd5\xc3\xff\xef\xa9\x6c\x0f\x37\x15\x94\x0b\x9e\xdf\x6c\x89\xfb\xef\xd8\x9a\xb7\x55\xeb\xd9\x9a\x6b\xcf\x5e\xfb\xc4\x59\x01\x7f\xe8\xcb\x9b\x28\x40\x0b\x76\xc6\xd7\xe7\xba\x97\x1e\xe9\x6c\x08\x10\xc1\x86\xfc\xef\xdf\x3f\x9a\xd0\x7c\x8b\xa2\xa0\xb9\xc2\xc6\x84\xe6\x75\x14\x08\x3e\x80\xa9\x4d\x14\x1c\xaa\x2f\x82\x7f\x43\x66\xd0\xa2\x96\xe8\xd5\x74\x4a\xf9\x37\xbe\x46\x0c\x2d\xf8\xe5\xfc\x0a\x49\x5f\xcb\x4d\x84\xf5\x80\x82\x3a\xb7\x79\x2c\x2e\xce\x52\xbe\xfa\x88\xe0\x56\x29\xb8\x55\x45\x7c\xf0\x09\xa4\xf3\x87\xce\x26\xf3\xe5\x25\xb4\x10\xff\x35\x35\xe8\x36\xee\x46\xb7\x21\x40\x2d\x97\x4d\x2b\x97\x3a\x22\xd4\xe2\xa9\x07\x66\xb9\xfa\xe7\x11\xcf\x87\xb7\xb2\xc0\x0b\xbd\xc8\xeb\xce\x77\xd6\x92\x86\x10\xbf\x28\x3b\x19\x95\xe8\xe1\x54\x70\x6d\x1e\xc3\xd4\xfd\x5a\x86\x5b\x3d\xe1\xb1\xe8\xed\x31\xea\xde\xbe\xad\xbf\x99\xf7\x35\xf5\x5d\x59\x5d\x96\x6b\x8e\x7e\x7a\xfa\x72\x0d\x18\xc6\x14\x53\x5f\x94\xa2\x0c\xe4\x23\xfa\x56\xe8\x57\xc8\xe5\x00\x04\xa3\x46\x12\x56\x54\x7c\x85\x16\xfc\x94\x55\xe5\xe2\xf4\x06\x04\x0f\xa8\xb8\x10\xbc\x52\xc1\xe1\x62\x59\x4d\xac\x52\x3d\x3a\x42\x8b\xe5\x68\xa6\x0a\x77\xb2\x48\x81\xfe\xa3\x91\xee\x3d\x23\x98\x14\xec\x3f\x6a\x21\x1b\x52\x52\x25\x19\x25\x98\xda\x1a\x5a\x75\xde\xeb\x70\xd7\xc9\x00\x6c\x5a\xf9\xf6\xa7\xef\x35\xad\xc0\x72\x02\x8c\xdb\xe7\x6c\x0d\xcb\x0b\x5b\xf9\x50\xa3\x29\xc0\x21\x5c\xa2\x51\x56\xb5\x14\x24\x6a\xbc\x37\xac\xfc\x6f\x7f\xfa\xfe\x66\x54\x2f\xd7\x76\x5a\xc5\xb3\x45\x3e\x61\x8b\x65\x35\xe3\x2b\xc5\x88\xcb\x0c\xd8\x22\xd7\xcd\x40\xf4\x70\xc4\x14\x5a\x3f\xbb\x2d\x05\x32\x66\x15\x8d\xe7\x29\xf8\xdf\xcd\x3e\x9e\x3e\xff\xdc\xe6\xf1\xf4\xf9\x67\xb2\x8e\xa7\xcf\x6f\xc6\x38\x96\xab\x8e\x6d\x2c\x57\x3b\x98\xc6\x72\x75\x6d\xcb\xf8\x6d\x47\xcb\xf8\xed\x77\xb6\x8c\xd7\x9f\xdf\x34\x5e\x7f\x36\xdb\x78\x7d\x53\xc6\xb1\xe9\x59\xc7\x66\x27\xf3\xd8\x7c\x82\x7d\xbc\xdb\xd1\x3e\xde\xfd\x4e\xf6\x01\x8b\xf2\xba\x65\x2c\xe4\xcc\xa8\x7a\x21\x9c\xf3\xa2\xda\x3e\x2b\x5b\x80\x4d\xc8\x6f\x68\x59\x34\x98\xe0\x0a\x9b\x9b\x32\x06\x40\x76\x33\xe6\x00\xa8\x3a\x06\x01\xbf\x3c\x9e\x90\xd0\x65\x07\x12\x48\x37\x85\x85\xc9\x0e\xc4\x2b\xd0\x02\xdd\x47\x3e\xb1\xad\x74\x69\x96\x32\x69\x4d\xe5\xfe\x7d\xb4\x80\x25\xf2\xc6\x18\xe4\xd6\x21\x82\x0e\xd0\xc2\x78\x59\xbd\xd9\x84\x04\x9e\xa1\xad\x7d\x44\xf5\xcb\x93\x9b\x21\x1d\xcd\x64\x81\x0e\x0c\x37\x86\x0e\x48\xf7\x97\xba\x04\xb9\xff\x4e\xeb\x85\xa9\xfc\x7f\x3b\xf3\x7d\x3e\xb1\xbf\x5c\xd4\xd6\xfb\xfc\x86\xac\x57\xea\xbd\x6b\xa9\x9a\xf1\xd6\xf6\xbc\x85\xf1\x0e\x22\x26\xa0\xba\x86\xfd\x6a\x5e\xd0\xe0\x19\x37\x60\x45\xfe\x77\xb7\xe0\xe7\xcb\x8a\x55\xfc\x73\x07\xe0\x15\x50\xb9\x29\x13\x06\x6c\x37\x63\xc2\x92\x31\xdd\x84\x57\xcb\xd1\xf8\x2b\x40\x46\xed\x57\xf5\x08\xec\x40\x45\xf5\xc5\x9e\x48\x07\xdb\x5f\x9e\x4f\xa2\x60\x60\x96\x9f\xaa\xb0\x1b\x8a\x39\x7f\x2c\x8d\x8d\x84\x1c\x01\xb1\xbb\xc2\x9e\x0f\x14\xf6\xf8\x3a\x0a\xfb\x36\xcf\x3f\x77\xe6\xcb\xf2\xfc\x33\x65\xbe\xf2\xca\xef\x9b\x78\x67\xce\x7b\xef\xcc\xf9\x4e\xef\xcc\xf9\xd6\xef\xcc\xfd\x11\x61\xbf\x49\x64\x61\xc3\xa8\x39\xf9\xcd\xd8\x6a\x75\x25\x9a\xd5\x63\x88\xbc\x18\xbe\x33\xac\xb4\xd7\xc3\x9b\x71\x0c\x13\xa9\xfd\x36\xe7\x46\xfb\x92\x86\xe2\xe1\x53\x23\xba\xfc\x66\x5e\x5d\xf9\x76\xa1\xae\x00\x5f\x16\xfa\xdc\xe6\xda\x74\xc3\xf1\x6a\x79\xce\x57\xd5\x15\xfa\xbb\xba\x62\x18\x00\xc1\xbc\x1a\x14\x83\x69\x45\x65\x20\xeb\x43\x13\x9e\x3a\xac\x34\x77\xa2\x77\xa3\xcb\xba\x3c\x5d\x94\x45\x99\xb1\x45\x85\x52\x78\x5e\x2e\x34\xdf\x00\xa2\x8e\xd9\xdf\x76\x5e\xba\x66\xa6\xfe\xe5\x06\xe6\x81\x87\x1c\xd8\xdd\xb1\x23\xae\xc9\xd3\x73\x61\x96\x6c\xbe\xd7\x91\xfd\xa8\xe0\x90\x31\x20\x37\x92\xd3\xd0\x6e\x25\x44\xde\x55\xf3\x27\xf8\xea\xa5\x2e\xea\x7e\x2f\x3a\x6b\xbe\x5d\x9f\xfd\x44\x64\x6f\x06\xed\xc5\xdf\xae\xd3\xda\xd3\x5d\xb1\x60\x8a\x13\xcc\x70\x0a\x67\x6a\x32\x9c\x63\x8e\x8b\xbd\x01\x92\xb7\xff\x46\x5d\x9d\x22\xec\x6d\xbd\x3c\x00\x46\x37\x6d\xcc\x76\x10\x96\x2f\xd5\xe6\x09\x08\x8b\xf5\x17\xf9\xdf\xdf\x7e\x33\x1c\xc0\x10\x79\x7f\xe3\x03\x7f\x3a\x46\xc3\x55\x30\xfd\x4f\x8e\xcd\x35\xf8\x71\xc3\x46\x7f\x2f\xa0\x35\x69\xef\x23\x90\x3e\x34\xe7\x8b\xd3\x6a\x86\xbe\x46\x74\xcb\xad\xd4\xfd\x40\x73\xb2\x5c\x7c\xe0\xab\xfa\xd5\x50\x0b\xc3\x2a\x3e\x88\x41\xbb\x3e\x1d\xb0\x55\xe0\xa9\x47\xed\x46\xbb\x9d\x95\xb9\x8f\xe8\x65\x37\x88\xde\x59\xa3\x9c\x55\x0c\xb1\xf5\x8e\x74\xb6\x9e\xc9\xea\xae\x14\x6e\xb4\x00\x7d\x58\x2d\x5f\xfb\xc4\xbe\x14\x02\x8f\x3f\x61\xcf\x8e\xa2\xd5\x35\x2a\xc3\xce\x9d\x1a\xee\xb1\x54\x66\xc3\x64\xad\x5e\xd3\x2e\x1e\xa9\x36\x03\x2e\xd9\xdd\xad\x37\xef\x77\x69\xbb\x4f\x7a\xb5\x4b\x78\x75\xab\x37\x83\x2d\xfc\xe2\xaf\xe6\xe1\xf0\xfc\x62\x3d\x9b\xd4\x89\x94\xc8\x11\x4c\xef\x95\x66\xe8\x5e\x2e\x81\x0c\xfb\x64\xeb\x54\x44\x53\x70\x1d\x41\x6a\x9c\xd3\xae\xdb\x58\x37\x92\x0c\xbc\x02\xd0\x08\x93\xcc\x96\xe7\x30\x48\x5a\xc6\x7e\x34\x9a\xb6\x36\x66\xcf\x51\x36\x5f\x2e\x5c\x6f\x2a\xdb\x9a\x34\xe0\xe9\xdb\x32\xfc\x68\xb7\x65\x78\xec\xb4\x65\x1d\x33\x64\x29\x92\xdd\x66\xe7\xab\x69\xa7\xeb\x09\xc0\xff\x19\x0c\xfb\xcf\x52\x32\x43\xa4\x75\x2c\x95\xf8\x86\x61\xb6\xde\x35\x66\x27\x00\x67\x98\xea\x85\x75\x99\x9c\x58\xc8\x34\x2e\x74\xd9\xf1\x9f\x51\x37\xb8\xdc\xc6\x07\x2e\x95\xc9\xd7\xe8\xdf\x94\x6f\x4d\x62\xb7\x9b\x2a\x00\x77\xd6\x97\x9b\xf4\xd8\xba\x6f\xa6\xb7\x5b\x46\x6d\x8d\xf9\xf8\x76\x4a\xc3\x6d\xf6\xbb\x1c\x7d\xfd\x27\x34\xab\xaa\xf3\xf5\xdd\xa3\xa3\xb3\x6a\xb6\x3e\x4c\xf9\xd1\x45\x55\xd0\x5f\xd7\xe8\x03\x39\xc4\x87\x04\xa5\x57\xe8\x7f\x9c\xb1\x6a\x56\xb2\xb5\xb0\x98\x76\x83\x0c\xec\x0a\x91\x9b\x3d\x8e\x8e\xd0\xf7\xbc\x92\xc7\xe1\x38\x17\xe2\x2e\x59\x3a\xe7\x6b\xf4\x57\x45\xe9\xaf\xb7\xbe\x82\x6d\xfc\x2b\xce\x1f\x34\xfb\x5f\x06\x3b\x69\xd0\x1d\xa9\xbc\x3b\xe8\xf6\xed\xfa\xe7\x7b\x76\xf4\xe8\xaf\xb2\x3b\x1a\xf2\x27\xf0\x43\x8b\xfb\x4c\x7d\xef\xa2\x56\xbf\xde\xbe\x6d\xd8\x9f\x73\xdc\x61\xb2\x01\x76\xb2\x71\x0a\x3b\x67\xfe\x3a\x95\xbb\xf1\x7f\x5a\xe6\xfc\xf0\xd7\x35\x5a\xae\xd0\x77\x72\x2b\x4d\x59\x94\x3c\x47\xd9\x32\xe7\x53\xc0\xc2\x16\x39\xba\x58\x73\x54\x56\x62\x5c\xfb\xab\x90\xa3\xd6\x07\xb5\x0f\xa7\xe9\xc3\xa9\xfa\xde\xed\x83\xfc\xf5\x9e\xdc\x93\xd4\x36\x3b\x6c\xa0\x8f\x75\x64\xbf\xfd\xa6\x7d\x3b\xbc\x2c\x17\xb9\x78\xbb\xec\xc0\xc8\xad\x43\x82\x17\xa4\xff\x0c\x9b\x7d\x6e\x7d\x75\xf4\xf5\xc1\x8d\xfd\x7d\x7d\x74\x4b\xf6\x76\x5d\xad\xca\xc5\xe9\xc3\xd5\xf2\xec\x64\xc6\x56\x27\xcb\x5c\x68\xee\x05\xfc\x78\x58\x68\xbf\x2a\xe1\xbf\x64\xef\xf9\x42\xca\xb8\x6f\xb2\xe7\x17\x8b\x2b\x21\xdf\x5b\x5f\x35\x11\xec\x22\x5b\x93\x9c\x8b\x1f\x27\x92\x8e\xec\x20\x2c\x6d\xc2\xe6\xfb\x7a\x08\x84\x9f\xb2\xe5\xc5\xa2\xe2\x2b\x35\x73\x09\x3f\xcd\xeb\x58\x21\x9b\xb7\xc1\x02\x9e\xc2\x79\xc6\xfa\x0b\xdf\x54\x2b\x26\xbe\x5c\xce\xca\x39\x47\x93\x1a\xdb\x7d\x85\x44\x92\xfe\x0a\xda\xb4\x08\x33\xd5\xbd\x6f\xab\xba\xc1\xfe\xbe\x70\xf5\xaf\x40\xa7\x12\xf8\x9b\x63\xe4\x6d\xbe\xa7\x9e\x27\x74\x2e\x7f\xba\x0f\x3f\x7d\xf7\xf0\xa1\xf8\xc9\x42\x49\x88\x0b\x5e\xd7\xd7\x17\xab\xd5\xf2\x94\x55\x7c\x0a\x56\x57\xcd\xf8\x8a\xc3\x39\x4f\xb4\xe0\x9b\x0a\x09\x16\x58\x56\xf1\x15\x34\x82\x6e\x6c\xc3\x1f\x30\x38\x91\xe0\xb7\x91\xb7\x79\x78\xe2\x79\x7b\xc2\x42\xbd\xcd\xf7\xf0\xf1\xef\x22\x38\xcf\x97\x97\x2d\x7d\x68\xf6\x95\x94\xbc\x1c\xca\x27\xaa\x8b\x02\x81\xff\xf0\xe1\x1e\x1c\xcd\xf4\xf6\xd0\x3e\xd2\x30\xc3\x83\xfd\xba\xe2\x90\xa2\xde\x66\xc1\xaa\xab\x17\x8b\x33\x56\x65\x33\x9e\xb7\xf4\xee\xa1\xe5\x62\x7e\x85\xd8\xf9\x39\x87\x7e\x97\x6b\x70\x40\x74\xb1\x28\xab\xa9\x78\xd1\xcc\xd8\x9a\xc3\xdb\xa6\x10\x44\x83\xa9\x81\x11\x42\xaa\xea\x7d\x51\x0d\x56\x31\xd4\x33\xed\xeb\x39\x2b\x57\xc3\x9e\x41\xbf\x14\xaf\x5f\x29\xd1\x1d\x1c\x28\xde\x6f\xf5\x3b\x60\x69\x29\x00\xc5\xff\x55\xbc\x97\x50\xb5\x37\x5e\xc7\x19\xf8\x02\x9c\x01\x46\xe1\xd6\x17\x1a\x2b\x97\x79\x4b\xd7\xc8\xcb\x45\xce\x37\xe8\x18\x1d\x60\xa3\xd9\x37\x7e\x74\xe7\x8e\x66\xfc\xfb\xfb\xb2\x99\xc5\xf8\x81\xce\x1b\x00\x79\xdb\x37\x76\x61\x4a\x0f\x85\xc6\xa5\x64\xe4\xaf\x07\xc7\xb5\xfa\xef\x69\xf2\x42\xfb\xc7\x86\xf8\x51\x23\xfa\xe6\x1b\x84\xbd\xda\x80\xd0\x6f\xca\x87\x94\x4a\x6a\x4e\xa4\xb1\xa2\xdf\x50\xc7\x0e\x1b\xe1\x6f\x41\x08\x10\xda\x94\xd4\x08\x3f\x9b\xf1\xec\xfd\x8b\x8c\xcd\xd9\xea\x7f\x89\x56\x13\xa1\x87\x67\xcb\x72\x21\x77\x53\x83\x00\x9a\x9f\xba\x1e\xdf\xfe\x2c\xbd\xbe\x15\x4e\x35\x5b\x2d\x2f\xd1\x83\xd5\x6a\xb9\x9a\x40\xaf\xee\x3c\x16\xa9\x50\x6b\x9a\x3f\xef\xdf\x41\xfb\x2d\x82\xc3\x6a\x29\x23\xeb\x04\x47\x7b\x87\xd5\xf2\xe7\xf3\x73\xbe\x3a\x61\x6b\x3e\xd9\x43\xfb\x12\x81\x30\xf9\xc5\xb2\x12\x06\x0e\xcc\x4a\xb9\xdc\x11\x0f\xeb\x8e\x7e\xfc\x0c\x23\x41\x2b\x27\xc8\xaa\x45\x26\xde\x8a\x63\x2a\x97\xd9\xd4\xe0\x24\xa5\x6c\xd0\xc6\x44\x17\xe0\x37\x75\x1b\xa9\x51\x98\xaa\xdc\x50\x6f\xaf\xaf\x17\xe9\x10\x27\x75\x43\x93\x5a\x34\xb4\xb7\x95\x71\x3e\x7c\x48\x55\xac\x53\x61\x0e\x1f\xa4\x57\x15\x47\x6b\xfe\x5f\x17\x7c\x91\x41\xa0\xb3\x33\xda\xd2\xa8\x4d\x07\x06\xc2\xab\xb3\x74\x39\x6f\x1c\xc9\x46\x99\x7a\x5d\xca\x64\x48\xb9\xc1\x34\x2e\xa4\x48\x0a\x08\x2b\x01\x9d\x78\x0d\x4b\xcd\xc6\x63\x03\x13\x10\x86\x75\x26\xfc\x21\x13\x0e\x83\xbf\xb7\x23\x93\x98\x48\x2e\x3d\xc5\xe5\x03\xaf\x83\x62\xff\xd8\x62\x35\xd1\x16\x9d\x79\xe0\x0d\x3a\x13\x7c\x92\x44\x31\x55\xcc\xc6\x92\xd9\x87\x5b\x32\x8b\xc9\xae\x9d\x6a\x21\x4d\x5c\x75\x3b\xda\xf5\x80\xc6\x36\x01\x43\xdf\x25\x44\xea\xaf\xc6\x89\x7e\xd2\xd4\x20\x15\xa9\xfb\x30\xb9\x1a\x64\x4d\x2d\xfc\xe8\xa0\xd2\x80\xd6\x3f\x08\x25\xc8\x68\xb5\xe5\xe0\xd2\xf6\x58\x27\xac\x8f\x32\x1a\xca\xfd\x63\x87\xeb\xf7\x22\x7a\xdb\xec\x73\x25\xc2\x8d\xec\x57\x9c\xe5\x27\xcb\x45\x55\x2e\x2e\xe0\xf0\x2c\x68\xbf\x0d\x45\x82\x93\x1f\xa0\xef\xdf\x1c\x03\x5b\x27\x22\xb1\x30\x8c\x06\x77\x7e\x58\x7c\x60\xf3\x32\x07\x20\x29\xed\x3b\xaa\x5b\x8d\xbc\xbb\x54\x90\x44\x08\x13\x05\x6f\x1a\x3a\x6f\x95\x9b\x88\xa6\xcd\x8f\xfb\xfb\x22\x19\xaf\x23\x54\x0f\xcd\x6d\x19\x46\x64\x22\x28\xa2\xe4\xdf\xb5\x60\x68\x84\xf6\x1f\x36\x8c\x1d\x1d\xa1\x1f\x0a\x74\xc9\x91\xc8\xd7\x2e\xce\x91\xc8\x54\xa7\xa8\xac\xfe\xef\xff\xfe\x3f\xf5\xb0\xa4\xa3\x00\x8e\x6f\x59\x7a\x3e\x00\xbc\x33\x08\xfe\xd2\x7a\x5f\x80\x17\x4c\x5a\x2b\x17\xc0\x58\x37\x43\xa2\x7f\xf1\xf5\x2f\x81\xc1\x7c\x87\xba\xfa\x04\x55\x75\x31\x1d\x0f\xb5\xae\x24\x5b\xb0\x39\x1c\x7e\x68\xe4\xf8\x9c\xb3\x1c\x15\xe5\x6a\x5d\xd5\x52\x82\x6e\xed\xae\xe6\xe1\xe8\x86\x26\x8b\xe5\x50\xbc\xeb\xbd\xda\x26\x24\xa1\xdb\x4a\xff\x2a\xb2\x6a\xbc\x36\xf2\xad\x79\x1d\x8e\x61\x3d\x3c\x0f\x6a\x83\x3a\xa9\x51\x81\x5a\xd0\xb1\xc5\x61\xee\xf5\xe3\x81\x8e\x0c\xcb\xd7\x0c\xa8\xb9\xd3\x68\xd7\x94\x80\x35\xd6\xdb\x9a\xaf\x16\xa3\xba\x09\xfc\x0e\x26\x58\xa7\xf5\xb2\xef\x7e\x5f\xb6\x67\xec\x0a\x95\x8b\x6c\x7e\x01\x2f\x21\xe2\xe5\x42\x7f\xa5\x31\x49\xf9\x61\x2d\x9d\x07\x3b\x48\x07\x4c\xf9\x7a\x02\xf4\xd4\x7b\x1a\x81\xbd\x49\x92\x96\x2e\x50\xdf\x26\x50\x0f\x92\x17\x29\xb0\xb1\xfc\xe0\x73\xca\x7c\x38\xc2\xf7\x25\x4a\x95\x44\x1f\xde\xac\x44\x21\x64\x5c\x53\xe8\x31\x08\xdd\xdb\xf4\xc5\xee\x6d\xbc\x93\x3d\xf4\x1b\x48\x64\x22\x79\x90\xbf\x36\xfa\x08\xac\xfa\x80\x37\x2a\xc3\x3b\x06\xf6\xf4\x57\x30\xb3\x26\x6a\x79\x1a\xb5\xf0\xf3\xcb\x87\x07\x14\xe5\x30\x53\xc6\xf3\x26\xf2\xd6\x61\x53\x9d\xc0\x6a\xbe\x43\x40\xd3\xbe\x43\xfc\xb9\xd7\xcb\x49\x54\xae\xd1\x8e\xc6\x92\xbf\x06\x5f\x37\x25\xd1\xc0\xea\xa8\x06\x54\xf4\x00\xa8\x25\x25\x5a\x8c\x6d\x67\x7f\x3a\xe9\x4e\x3b\x4f\x54\x9d\x9d\x6b\xd9\xc8\xa4\x3a\x3b\x47\xc7\xbd\xb1\x64\x0f\xfd\xe9\xf8\x58\x06\xe5\x7e\x76\xa2\x16\x31\xaa\xb3\xf3\x7e\x9e\xa1\xbd\xa0\xb7\xd0\x7b\x9f\x73\xf2\x4d\x88\x15\x1d\x03\x83\x77\x3e\xf0\xd5\xba\x5c\x2e\xee\xdc\x45\x77\x60\xd2\xf7\xce\x54\xfc\x2a\xf9\xb9\x73\x57\xcb\x0a\xe1\x77\xd9\x5d\xf5\xbb\xfc\x72\xeb\xab\x8f\x6a\x92\xee\xc5\xf2\x8c\xa3\x6f\x9f\x7c\x8f\xd2\x8b\x72\x9e\xa3\xe5\x79\x55\x9e\x95\x7f\xe3\xab\xf5\x14\xcd\xcb\xf7\x1c\xad\x0e\x7f\x5d\x4f\xe5\x2b\x31\xcc\xb4\xaf\xcf\x79\x56\x16\x65\x26\x9c\x37\x2f\x41\xe1\xe7\xac\xaa\xf8\x6a\xb1\x06\x7c\xd0\xa8\x9a\x71\x54\x2c\xe7\xf3\xe5\x65\xb9\x38\xbd\x2b\xe7\x3c\x85\xf9\xf5\xce\x45\xa2\x3b\xb5\xd1\xdc\x91\x93\xbb\x1d\x80\x43\x76\x96\xf7\x66\x51\x9b\x23\x92\xe2\xd9\xad\xaf\xa4\xba\xd4\xa1\xc9\x66\x9a\xbb\x3b\x80\x89\x3e\x83\xee\x40\x39\xed\xdb\x45\x6f\xd6\xf8\x4f\xda\xf7\xc3\xc5\x32\xe7\x2f\xaf\xce\x79\x9b\xcc\xb5\x73\xd5\xea\xc5\xa3\x5c\xe8\xf3\xc6\xcf\xcb\xc5\xe9\xf2\x7f\xbe\x40\x1f\xbc\x43\x7a\xe8\xc1\xeb\x79\xdb\x42\x3b\x4b\xda\x30\xa3\x42\x63\x8d\x89\xad\x2e\x67\x6c\xde\xc3\x14\x1f\x7a\x07\x72\x22\x66\x55\xef\x8d\x92\xa7\x18\xd5\x6f\x33\xb6\x7e\x7a\xb9\x78\x56\x6f\x81\x39\x56\x40\x87\xdd\xdf\x01\xbc\x59\x22\x81\xaa\x71\x52\x28\x75\xc4\xe8\x82\xcb\xf5\x21\xf1\x1c\x0e\x12\xef\x09\xd9\xe8\xb2\x7a\xf3\x5e\x16\x30\x14\x10\xf0\xb9\x33\xf9\xd5\xeb\xd7\xf3\x59\xb9\x58\x8a\x5e\x31\x74\xc9\x53\xa4\x0e\xaa\xaa\x59\xeb\x43\x65\xd0\x4a\x26\x1f\x6f\xa9\x23\xaa\xb0\x6c\xf2\x71\xfa\xf7\x8f\x6f\xa7\x34\xda\x66\x49\x64\x70\x62\xf7\xf5\x93\xc7\x8f\xaa\xea\xfc\xb9\x18\x32\xd6\x55\x83\xed\xcf\x69\x79\x2a\x37\xb3\x1c\xfe\xba\xfe\xf3\x36\x98\xef\x5c\xac\x39\xbc\xb0\x65\xd5\x9d\x7b\xb7\x86\x84\xbe\x2b\x4f\x7f\x02\x84\xf7\x44\x87\x7f\x5d\xcf\x44\x50\x2e\x4f\x17\xcb\x15\xbf\x3b\x2f\x17\xfc\x56\x43\xfa\x92\xa7\xfe\x56\x24\x85\x92\x5e\xf1\x54\x8e\x4d\xf2\x98\xf1\x9d\xc3\xa3\x79\x99\x1e\x09\x14\x22\x38\xdf\x3a\x3a\x42\xf9\x72\x71\xa7\x42\xcb\x0f\x7c\xb5\x2a\x73\x5e\xaf\x38\xd4\x0b\x1c\xb7\xb4\x33\xc8\x6a\xe9\x40\x44\xb8\x3b\xcd\x8e\x06\x58\x90\xe8\x00\x1c\x4a\x9a\x5d\x28\x61\x21\xb0\x4e\xa6\x83\x00\x77\xf7\x6e\x7d\x34\x88\x43\x3e\x51\x2b\x5b\x35\xcb\x7f\xbe\x4b\xc8\xc7\xb7\x42\x0c\xd3\x37\x52\x0c\x6f\xf7\x6e\x1d\x1d\xfd\x7f\x68\xbd\xbc\x58\x65\xfc\x09\x3b\x3f\x2f\x17\xa7\x3f\x3f\x7f\x7c\x2c\x1e\x1e\xcc\x61\x17\xe9\xaf\xeb\xc3\x33\x76\x7e\xeb\xff\x05\x00\x00\xff\xff\x73\x19\x9e\x3a\x8a\x21\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\x73\x60\x18\xc6\x99\x0c\x1b\x02\x33\x64\x6f\x20\x5c\x40\x76\x76\xef\x6c\x96\xab\xed\x96\xed\x1e\xda\xdd\xfe\x75\xb7\x73\x18\x92\xef\xfe\xbf\x54\x3a\x95\x0e\x7d\x70\x12\xe6\xb4\xc9\x0b\x70\x4b\xa5\x53\xa9\x54\x2a\x95\x4a\x55\x19\xfd\x7f\xcb\x28\xa3\x7b\x9d\xc9\x32\x19\x17\x51\x9a\x10\xda\x29\x7a\x49\x2f\xeb\x7e\x51\x29\x79\x27\xed\x2d\xbb\x5f\xa2\x49\x67\x3d\x39\x49\x4f\xf9\xaf\x02\x7e\x9d\x05\x19\x09\xf6\x8a\xcb\x05\x4d\x27\x44\xd6\xb5\xd7\x92\x45\x5b\x0f\x1e\x88\xc4\x5d\x56\x66\xf9\xe0\x41\xd0\xcd\x68\xb1\xcc\x12\x12\x74\xd2\xde\xfa\x66\x97\xa5\x47\x32\x2d\x12\x69\xac\xd6\xc9\x5e\x42\xcf\xc9\xcb\x2c\x4b\xb3\x4e\xeb\x20\x48\x92\xb4\x20\x93\x28\x09\xc9\x3c\x0d\x97\x31\x25\xed\xd6\x46\xba\xd1\x6a\xb7\xba\xbb\xc5\x2c\x4b\xcf\xc9\xa4\x3f\x4e\x43\xba\xd7\x7a\x73\xf4\xe2\xf8\xf5\xcb\x4f\x6f\x8f\x3e\x7e\xfa\xf1\xe8\xf8\xed\x8b\x56\x6f\x72\xcd\xea\x8b\xf7\x58\xdf\xf7\xbe\xd0\x8b\x45\x9a\x15\xf9\xf0\xcb\xf5\xf5\x2e\x1b\xc3\xc9\xe6\x69\x7f\x1c\xc4\x71\x27\xee\x8b\xac\x9e\xec\x7d\x87\xf2\x01\x26\x7b\x00\xb8\x75\x7a\x42\x4f\x77\x45\x57\xf3\x4e\xf2\x2c\x19\xd2\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\xdb\xdc\x4d\xbf\xcf\xfa\x31\x4d\xa6\xc5\x6c\x37\xdd\xd8\xe8\xe6\x9d\x8c\x21\x5e\x75\xe3\xba\xdb\xf9\xb2\x35\x3c\x51\x5d\x16\x55\xf4\x38\x96\x7a\xa2\xed\xee\x97\x35\x9e\x20\x3b\xb3\x77\xb2\x46\xc8\x97\x35\x42\x08\x69\x8d\xd3\x24\x2f\x82\xa4\x68\x0d\x49\x91\x2d\x69\x8f\xa7\x46\xc9\x62\x59\xe4\xad\x21\x39\x81\x6f\x09\x0d\x79\x49\x30\xa7\xad\x21\x69\x7d\x4a\xcf\x13\x9a\xb5\x7a\x3a\x87\x8d\x8e\xe5\x04\x61\x98\xd1\x3c\x6f\x89\x9c\x6b\xf8\xff\x54\x54\x2d\x8b\xc3\xff\x22\x2d\x5d\x16\xf5\xed\xa5\x9f\x50\x11\xa3\xbd\xd1\x65\x41\xf3\x9d\x6d\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\xd1\x7b\x3a\x8d\xf2\x22\x0b\xfe\x0b\x26\xaf\x57\x55\x07\x3d\x3f\xba\x15\xdf\x2f\xb2\x20\xc9\x27\x5e\xd6\xf7\x67\xc1\x41\x66\x91\xc2\xea\x48\xc8\x69\xf1\xa1\x9a\xa4\xee\x0c\x17\x76\xd3\xbf\x49\xa3\x5f\x79\x02\x82\x26\x88\xaf\xaa\x60\x91\x45\xf3\x20\xbb\xf4\xf6\x23\x4d\xe3\xda\xc9\xdb\x17\x6d\xfd\x79\x51\x68\xee\xc1\x95\xd5\x94\x21\xe1\xa0\x74\x1b\xff\x23\x21\xc1\xdb\xfb\x30\xca\xd3\xf3\xe4\x16\x3d\x0f\x92\x34\xb9\x9c\xa7\xcb\x7c\x85\xae\x47\x49\x48\x2f\x68\x68\xec\x5d\x77\x36\xb1\xba\x72\xd4\x1d\xb3\xf6\xf3\x28\xb9\x0d\xe3\xde\x5f\x02\x26\x5e\x26\x21\x0d\x5b\x16\x9a\xe8\x19\x23\x84\xbf\x00\x8e\x46\x51\x18\x36\xc3\xd1\xcd\xea\x3f\x0b\xe2\xa5\xb7\xfb\xcb\x28\x29\xb6\xbf\x79\x52\x3d\x05\x6f\xe9\xf9\xf3\xe8\x77\x44\xfe\xad\xd6\xdc\xc1\x2c\x48\xa6\xbf\x27\xe9\xdc\x09\xe5\x94\xd4\x8d\xa4\xfa\x4a\xaa\xf1\x62\xe6\x1d\xdf\x8d\x6a\x11\xb4\x76\xba\xb6\x76\xdd\xfb\x72\x7d\xda\xdb\xfe\xdd\x0e\xfd\x7f\xa1\x33\xef\xef\x24\x3b\x4e\x96\x49\x78\x63\x52\xb9\xf5\xc6\x75\x7f\xec\xfd\x73\x1f\x7b\xef\x0f\x7d\x7f\xe4\x33\x87\x77\xf0\xe2\xbc\xf0\x47\x93\x36\xbf\xee\x66\xae\xf7\xaa\x9d\x3b\xdb\xab\x56\x9d\xf7\x49\x96\xce\x6f\x39\xed\x45\x7a\xcb\xa3\xe6\xed\x04\xbe\xdf\x77\xdd\xfc\x11\xf0\x17\x25\x61\x94\xd1\x71\x71\xe8\xdd\x33\x57\xe8\xc9\xed\x26\x22\x1a\x07\x8b\x8f\xbf\xeb\x64\xf8\x31\xd9\xec\xb4\x4b\x17\x69\x1e\x55\x1d\xd4\x17\xc1\x65\x30\x8a\xa9\x29\x14\xfc\x2e\x5c\xa9\x8c\xe6\xee\xe4\xf8\x75\x3b\x1a\xd8\x97\xe3\x7d\x61\xe2\xf3\xb7\x3f\xc9\xdc\x09\x92\x4a\xea\x6e\x46\x67\xbf\x03\xfa\xff\xb0\x58\xbf\x8b\xf3\xe3\x8d\xf9\xe4\xd7\xc6\xba\xcd\xf4\xee\xd1\xde\x10\xed\xb7\xde\xb8\xbe\xf6\xcc\x1e\x7a\xb6\xb4\x2a\x39\xee\x71\x13\x39\x0e\x8c\x37\xc8\x9e\xb4\x70\xe8\xb4\xfb\x83\x49\x9a\xcd\x83\xa2\xa0\x59\xde\xee\xee\x02\xc0\x87\x34\x8e\xc2\xa8\xb8\xfc\x78\xb9\xa0\x26\x2c\x6b\x9f\x41\xad\x0d\x1e\x3e\x5c\x23\x0f\x0d\x48\xa1\x73\x27\x51\x4e\x02\xb2\xc8\xd2\x94\x01\x93\x62\x16\x14\x24\xa3\x0b\x76\xc8\x4a\x8a\x9c\x88\xb9\x23\x2c\x93\xd5\x70\x58\x90\x79\x50\x8c\x67\x34\x1f\xb2\x4f\x91\x8d\x7e\x9e\x9c\xe2\x8f\xc7\xc6\xd7\xa9\x99\xb9\x63\x7d\x9f\x9e\x3c\x39\x3d\x39\xed\x91\x7e\xbf\xbf\x46\x1e\x0e\x9c\xb1\xc9\x1e\xef\x11\x65\x4d\xd3\xe9\x8a\x29\x2e\x66\x51\xde\xff\x04\x0b\xe3\x47\x89\x20\x06\xd8\xe7\xe8\x3a\x64\x19\x87\x49\xb1\x8b\x80\xf9\xbe\xed\x83\x3e\x82\x1c\xd1\xdc\xee\xda\xf5\xee\xda\x9a\xa7\x1f\xfd\x45\x96\x16\x1c\x6b\x7b\x24\xa1\xe7\x46\x5f\x3b\x5f\xae\xbb\xbb\xd5\xa5\xfa\x20\xbd\x64\xcb\x71\x91\xb2\xc6\x3d\xb0\x75\xed\xf6\xa3\x5c\xcc\xb9\x46\x08\x23\x47\x89\x14\x61\xd7\xb2\xbe\xce\x12\xfb\x30\x6f\x9d\x81\xc0\x76\xe7\xdf\x27\x9d\x93\xcd\x47\xdf\x9d\x3e\xec\xfe\xfb\xb4\xfb\x6c\xd0\xe5\xe3\x34\x0f\x0e\xa5\xdd\xba\xee\x7d\x69\x61\x52\x6c\x0d\xbf\xeb\xb5\x38\xbd\xb5\x86\x5b\x8f\xaf\x4f\x7b\xdf\xfc\xce\xe4\xfd\x3c\x4d\xe3\x1a\xda\x1e\x31\x90\x12\xc2\x66\x79\xf2\x7f\x4e\xa5\xf0\xeb\xb1\xfe\x79\x8a\x92\x77\xf0\x47\x1d\x19\x43\xcf\x6e\x4a\xc3\xac\xf0\x2a\x44\xcc\xe1\x6d\x0a\x66\xa9\x2b\x92\xaf\x59\xa4\x82\x76\x79\x8b\x55\x65\x6f\x42\xb5\xff\x61\xa8\x35\x69\xf6\xe1\xff\x34\x22\x5a\xd1\x9f\x7a\x8a\x7d\xf2\x7b\x53\x2c\xdb\xc3\x14\xc9\x16\x7e\x9a\x2d\x66\x94\xc0\x66\x07\x84\xdb\xf7\x51\x2e\xcb\x55\x3f\x04\x5d\xc2\xcf\xc7\xe8\xf7\x29\xce\xd8\x31\xbe\x4c\xfa\x25\x62\x6b\x55\x3f\x9f\x1a\xf5\x88\xa2\x1e\x2a\x87\x4e\xde\x98\xcc\x59\xe9\x95\xe8\x9c\x17\x70\x08\x9d\x25\xaf\x4a\xe9\x66\x99\x2a\x52\xe7\x8d\x56\x96\xbe\x19\xb1\xb3\x4a\x38\xa9\x7f\xd9\xea\x5d\x77\x6f\x46\xf8\xa2\x77\xf5\x94\xff\x6d\x13\xca\x1f\x3c\x84\x0e\x7f\x9c\x45\x39\x99\x44\x31\x65\x94\xba\x08\xb2\x82\xa4\x13\x72\x4e\x47\x3b\xfd\x5f\xf2\xfe\x1a\x80\x88\x2f\x06\x30\xc9\x28\x25\x79\x3a\x29\xce\x83\x8c\x0e\xc9\x65\xba\x24\xe3\x20\x21\x19\x0d\xa3\xbc\xc8\xa2\xd1\xb2\xa0\x24\x2a\x48\x90\x84\x83\x34\x23\xf3\x34\x8c\x26\x97\x50\x47\x54\x90\x65\x12\xd2\x0c\x08\xbe\xa0\xd9\x3c\x67\xed\xb0\x8f\x9f\xde\x1e\x93\xd7\x34\xcf\x69\x46\x7e\xa2\x09\xcd\x82\x98\xbc\x5b\x8e\xe2\x68\x4c\x5e\x47\x63\x9a\xe4\x94\x04\x39\x59\xb0\x94\x7c\x46\x43\x32\xba\x14\x54\x44\xc9\x8f\xac\x33\x1f\x44\x67\xc8\x8f\xe9\x32\x09\x03\x36\xe6\x1e\xa1\x51\x31\xa3\x19\x39\xa3\x59\xce\x66\x68\x47\xb6\x25\x6a\xec\x91\x34\x83\x5a\x3a\x41\xc1\xc6\x90\x91\x74\xc1\x0a\x76\x49\x90\x5c\x92\x38\x28\x74\x59\x17\x05\x7a\xa4\x21\x89\x12\xa8\x76\x96\xca\x95\x1d\x15\xe4\x3c\x8a\x63\x32\xa2\x64\x99\xd3\xc9\x32\xe6\x82\xe3\x68\x59\x90\x9f\x0f\x3f\xbe\x3a\x3a\xfe\x48\xf6\xdf\xfe\x8b\xfc\xbc\xff\xfe\xfd\xfe\xdb\x8f\xff\xda\x25\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\x3f\x78\xb5\xff\xf6\xe3\xfe\xf3\xc3\xd7\x87\x1f\xff\x45\xd2\x8c\xfc\x78\xf8\xf1\xed\xcb\x0f\x1f\xc8\x8f\x47\xef\xc9\x3e\x79\xb7\xff\xfe\xe3\xe1\xc1\xf1\xeb\xfd\xf7\xe4\xdd\xf1\xfb\x77\x47\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\x27\x24\x49\x8b\x1e\xc9\x29\x25\xdf\xcf\x8a\x62\x31\x1c\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\xed\xcd\xad\x6f\x80\xe1\xd5\x6f\x05\x15\x02\x2c\x2a\x23\xe4\x31\xdf\xde\x21\x24\x05\x04\xcc\x76\x41\x1f\xe4\x61\x52\x98\x80\x51\x52\xf8\xe0\x8e\x1d\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\x76\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\x3f\xcb\x82\x4b\x0e\xce\x99\xb8\x25\x0a\x1c\x30\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\x07\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\x64\xf3\x74\x97\xf3\xcf\x68\x42\x3a\xeb\xb8\x11\x5c\x07\x7f\xa0\xc1\x9f\x72\xb4\xa3\xe4\x2c\x88\xa3\x50\xd3\x00\xab\x71\x7d\x48\xda\x64\x83\xe0\xca\xd7\xb0\xac\x81\x6b\x36\x29\xb0\x84\xd2\xc8\x22\x0e\xa2\x84\xd3\x97\x35\x8d\x1c\xe0\x9d\xc8\x29\x9f\x45\x91\x7e\x34\xfa\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\x61\xc0\xa7\x3d\x72\x02\xe0\xa7\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\x27\x50\xe4\x94\xb3\x67\xf9\xa5\x56\x11\xea\x8f\xd8\xa7\x8e\x26\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\x67\xab\x4b\x06\x64\x67\xbb\x4b\x1e\x92\x9d\x6d\x31\x68\x84\xbe\x60\x3c\x26\x1b\xa4\xa3\x36\x1d\x03\xeb\x25\x28\x24\xcf\xd0\xde\x45\xc8\xce\x36\x19\x1a\x09\x25\x9d\x95\xa8\xef\x91\x4d\x8c\xfd\x8c\xe6\xcb\xb8\x90\xd4\xc3\x67\xf0\xcd\x32\x2e\xa2\x9f\xa3\x62\xc6\xe7\x44\x52\xa0\xd1\xb7\x9e\xa2\xa3\x9e\x39\x83\xb2\x72\x31\x42\x5e\xbf\x79\xe2\xf3\x93\xbe\xd5\xaa\x6f\x0d\x34\xec\x01\x5a\x23\x6a\x78\xad\xd6\xae\x5e\x38\x34\x9e\x88\x11\x8b\xce\x8a\x5d\x21\xcd\x5e\x06\xe3\x59\xc7\x66\x4c\x11\xa6\x2d\xc6\xf5\x4b\xe7\x4b\xcf\xd5\x69\x17\x17\xe2\x08\x81\xae\x6c\xb8\xda\xce\x8e\xd9\x7d\xb9\x8e\x10\x11\xaa\xb5\xcb\xa8\x98\xc6\x13\x01\x62\xcf\x11\x74\xc0\xed\x92\xc4\x13\x7c\xd8\x93\x85\x9b\x30\x97\xe2\xc6\x1e\xa1\xe2\x19\x1e\x19\x90\x6d\x0d\x7a\x4d\x68\x9c\x53\x6b\x78\x83\x01\x09\xd3\xa4\x5d\x90\x20\x0c\x89\x28\x55\xa4\x66\x95\x7d\x12\x15\xed\x9c\x04\x71\x46\x83\xf0\x92\x8c\xd3\x65\x52\xd0\xb0\x04\x4b\x5f\x69\x9c\xd7\x7a\x11\x0e\x06\xe4\xe3\xd1\x8b\xa3\x21\x99\x44\xd3\x65\x46\x09\x3b\xb0\x25\x34\x67\x27\x40\x76\x4a\xbb\xcc\x4d\x66\xf5\x5b\x10\xc9\x1f\x67\x92\xcd\xc9\xa0\x18\x81\x12\x2b\x25\xcb\x5c\xa1\x35\xa3\x93\x00\xd4\x31\xe7\xb3\x34\xa6\xbc\x87\x51\x32\x5d\xaf\x61\x04\x15\x3c\xc0\xe6\xfc\x62\xd0\x3d\x92\x3a\x2b\xdf\x58\xe4\x72\x4e\x6a\x45\x7d\xcf\x16\xd7\x71\x55\x63\x88\x80\x78\xc3\xe4\x3c\xd0\x64\x9d\xd3\xc2\x99\x53\x4e\x56\x6f\x83\x39\xb5\xf7\x21\x9d\x83\xe5\x4c\xb7\xac\x67\xf3\xa9\xde\xcf\x74\xc5\x9e\x3a\x15\x5f\x14\x18\xd4\x52\xad\xfc\xab\x18\xb6\xac\x64\x91\xd1\xb3\x28\x5d\xe6\xaa\x43\xdb\xbb\x0c\x25\x51\x42\xa2\xa4\x70\x4a\xd4\xe1\x1f\xf5\xd7\xd7\x20\xfb\x9b\xa4\x19\x81\x47\xc2\x11\xd9\x23\x5b\xbb\x24\x22\xdf\xcb\x01\xc8\xf7\xc2\x24\xda\xd8\x28\x2b\xce\xfe\xac\x3e\x6f\xec\x91\x8d\x8e\xc4\x41\x44\x1e\x91\xad\x53\x26\xe1\x93\xab\x2b\xb2\xb9\x5b\x5a\x49\x05\x2b\x17\xf4\xb0\x41\x22\xf2\xb0\x6c\xe6\x36\xec\x5e\x30\xe1\xa0\x8c\xed\xcb\xbf\x6b\x27\xd5\x4c\xb9\xee\x76\xba\xd6\x14\x0e\x06\x64\x12\x65\x79\x41\x68\x4c\xe7\x34\x29\xd8\xf9\x8a\xa3\xa9\x47\xf2\xcf\xd1\x82\x44\xc5\x2a\x53\x6e\x60\x7f\xd3\x87\x7d\x86\xbf\xca\x19\x80\xa7\xf3\x61\x18\xb1\x46\x82\x58\x2d\x72\x81\x4f\x87\xff\xb8\xf8\xf6\xf3\x45\x4d\x3a\x25\x0c\xe2\x24\x22\x1b\x64\xeb\x54\xf2\x09\xb2\x41\x9c\x6e\x78\xd0\x5e\x8b\x60\x8b\xf9\x79\x20\xc5\x56\xe9\xa1\x7d\x4e\x15\x37\x66\x3d\x7f\x68\xa6\xc2\x84\x2d\x13\x53\xb7\x5c\xfc\x35\x94\x49\xca\x18\xd2\x66\x15\x43\x22\x8d\x68\xba\x96\xa3\x0c\x06\x64\x1c\xc4\xe3\x65\x1c\x14\x54\x0a\x3e\xec\xc8\x27\xfa\x42\xa2\x82\xce\x6f\xc1\x8e\x18\x2b\x3a\xf9\x13\x31\xa5\xae\x0d\x7b\xbd\xd2\xbe\x72\xcb\x09\xf9\xfd\x18\x0c\x66\x2e\x5f\x9d\xb7\x10\x47\x5b\x24\xfa\x51\xa3\x0d\x11\xba\x48\x71\x33\x99\x56\x68\x8c\x38\x64\x63\x8d\x91\x4c\x57\xb7\x9a\x4a\x25\xe2\xd7\x25\x95\xeb\x41\x50\xc3\x1e\xf1\x0f\xea\xf7\xe9\x88\x50\x31\xad\x23\xe2\xd0\x20\xdb\x34\x41\x4b\xa5\x92\xa8\x04\x21\x65\x3a\xa2\x72\x84\x88\x12\x70\xc2\x80\xd6\x34\x62\xaa\x35\x44\x78\x88\xbe\xd3\xb1\x81\x9b\xd5\x15\x44\xb2\x14\xa7\x62\x0c\xcf\x89\x38\xf7\x9e\xc2\xad\xe3\xfe\x1d\x6b\x94\xf8\x90\x3b\x30\x32\xb9\xbe\xb4\x5a\xc4\xd0\x8b\xc8\x1a\xb5\x86\xa9\x4a\xe5\xa0\x47\x55\xab\x67\xc0\x18\xe5\x1c\x88\x95\xb9\xeb\x91\x36\x51\x47\xa9\x93\xa8\x4f\x0e\x16\x5d\x2b\x65\x92\x83\x01\xc9\x97\x73\x7e\x43\xe7\xd9\xa5\x84\x88\xa8\xe0\x45\x75\x27\xd1\x29\xe3\x8a\xea\x0b\xb6\x24\x1f\xff\x91\xcd\x9b\x88\x90\xd2\xa6\x83\x82\xc1\x80\x64\x74\x9e\x9e\xc1\x35\x26\x19\x2f\xb3\x8c\xc9\xa7\x4a\x38\x4d\x21\x59\x74\x33\xca\xa1\xe7\x9e\xde\xe6\xab\x68\xfc\x24\x32\x1b\x6b\xfe\x8c\x91\x91\x47\x4e\xfd\x8d\x29\xed\x83\xb5\x0e\x4b\xae\x75\xbc\xa7\x56\xc9\xe3\x3c\x54\x56\x58\x57\x0e\x92\xac\xd8\x0e\x86\x2f\x49\xcc\xfb\x0b\xde\x5b\xd6\xd6\x58\xdc\x32\x61\x53\x0b\xe8\x7d\x87\xdb\xab\xda\x26\x18\xe2\x5a\xb4\xd3\xed\x79\xb3\x9f\xa7\x69\x5c\x96\xc7\x84\x90\x92\xac\xe3\x8a\x3c\x7c\xb9\x59\xda\x6c\x55\x26\xe7\xc2\x65\xb9\xef\x69\x50\xda\xe3\x63\x9e\xb9\xc6\x08\xc2\xb5\xdf\x00\xd4\x29\x9b\x0d\x69\x38\x3b\x7c\xdc\x6b\xf1\xbb\xdf\xd6\xf0\x1b\xf8\xc9\xfa\xd6\x1a\x3e\x61\xbf\xf1\x75\x6c\x6b\xf8\xb4\xe7\xb3\xf5\x88\x92\xa2\x35\xdc\xda\x64\x3f\x33\x1a\xc4\xad\xe1\xd6\x36\xfb\xcd\x6f\x65\x5b\xc3\xad\x1d\xf6\xb5\xe4\x50\xd0\xc0\x52\x80\x3d\xb9\x3e\xed\x3d\xfd\x2d\xed\xa2\x6a\xae\xa1\x6f\x66\x4d\x84\x2b\x59\xc5\xa8\xc8\x2c\x67\xdb\x16\xe1\xdc\x15\x4d\x8c\xfc\x45\x2b\x2c\x8d\xcc\x9e\x34\xa9\xeb\x16\x76\x47\x25\xc6\x46\x8d\x1a\x45\x57\xe2\xde\xe9\x92\x6c\x27\x5b\xd2\x06\x26\x4c\xd6\xb0\xeb\x2d\x99\xbe\xbb\xb7\x64\xba\xb7\x64\xfa\x6f\xb1\x64\xd2\x0b\xe1\xae\xcc\x99\x9e\x47\xd3\xb7\xcb\xf9\x08\x58\xa1\xe2\xce\xa3\x68\x9a\x40\x62\xff\x17\xc5\xc9\x97\x45\x14\x9b\xf6\x35\xfd\x01\xa4\xf1\x7f\x25\xd8\xd8\x0b\x32\x4e\x93\x49\xe4\x18\x03\xc9\x93\x19\xda\x15\xe0\xec\x02\xdb\x82\x1c\x38\xe7\xd5\x39\x01\x7e\x4f\xe0\xc1\x06\x3b\x67\x31\xbe\xa5\xad\x64\x61\x29\xb0\xb9\x01\xe5\xcc\x43\x86\x63\x0e\x19\xe5\x24\xa1\xd3\xa0\x88\xce\x68\x4f\x72\x22\xb8\x38\x2a\xce\xd3\x76\x4e\xc6\xe9\x7c\x21\xa5\x55\x28\xc5\xe6\x56\x95\x9c\xc4\x69\x50\x44\xc9\x94\x2c\xd2\x28\x29\x7a\xfc\x3a\x94\x91\x7d\x98\x9e\x27\xd6\x99\xce\x54\x93\xb8\xc7\xb7\x2b\x8e\xe5\x2b\x85\xef\x6b\x39\x16\xb6\x94\x12\x4a\x43\x38\x45\x8f\xf4\x1c\x87\x7e\x63\x18\x40\xda\xb5\xb2\xf3\x31\xdb\x35\x18\x30\xd4\x2f\xb9\xb0\x6a\xb7\xcf\xe7\xa2\x33\xee\xbf\xfc\xf8\xea\xd3\xf3\xc3\x9f\xde\x1e\xbf\x79\xfe\xf2\xfd\xa7\xf7\x47\xc7\x6f\x5f\x1c\xbe\xfd\xe9\xd3\x9b\xa3\x17\x2f\xd1\x19\x4e\x69\xe2\x60\x26\xfb\x8b\x20\x7c\x4d\x27\x45\x87\x7f\x15\xe9\xc7\xf3\x34\x3f\x50\x58\x14\x6d\xf6\x8b\x54\x88\x4b\x5b\x4f\xba\x3d\xf2\xe4\xb1\x79\xc3\x83\x77\x4b\x18\x4e\x87\x37\x62\x1a\x60\x98\x13\x2f\x0f\xbf\x25\x38\x7f\xae\xce\xc6\xe6\xa1\x79\x55\x1c\xba\x52\x87\x81\x45\x0f\x42\x8a\xf4\x15\xbd\x90\xe3\xce\x97\xa3\xbc\xc8\x3a\xdb\x08\x7f\xb1\x75\xb5\xcf\x8b\x4b\x2d\xf7\x06\x79\xb2\xd3\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\xb8\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\xde\xbc\xdd\x5f\x9b\x6c\x88\xea\xc9\x33\x42\xda\x5b\x6d\x32\x24\xed\xcd\xf6\xed\x79\x54\x1d\x26\xd9\x89\x95\x95\xfa\x07\x83\xcb\x09\x13\x8c\xe7\xcb\xb8\x88\xb8\x50\x39\xba\x24\xdb\xff\x99\x33\xf1\x5c\xd9\xd0\x05\xac\xe6\x82\x4e\x69\x56\xb1\x95\xbc\x17\xb5\xd6\xed\xdf\xab\xce\x88\xb0\x65\x2e\x99\x11\x81\x26\x8b\xfa\x18\xd6\x54\x8b\x6a\x73\x8d\xe6\x34\xb7\xb2\xb6\xbb\xfd\x45\x7a\xde\xd9\xda\x7e\xda\xed\x9a\x28\x3d\x98\xd1\xf1\x67\x12\x4d\x0c\x9c\x22\xb1\xc8\x42\x44\x1e\x4d\x13\x1a\x1e\xe6\x6f\x75\xb6\xa3\x88\x56\x75\xcc\xe8\x85\xe8\xb1\x89\x0c\x49\xb4\x70\xe8\x83\xb6\x0b\x53\x12\x4b\xd9\x91\xe5\x3c\x62\x62\x78\x10\xe7\xda\x6a\xd9\x6e\xbd\x16\x5f\x3e\x0c\x49\x76\xb3\xd9\x23\x5b\xdd\x1e\xd9\x7a\x82\xe4\x91\xed\xae\x91\xdb\x25\x7b\x7b\x7b\x8c\x64\xbd\x54\x98\x31\xf6\xf1\x28\x88\xa1\x53\x84\xab\x0e\xf4\x85\x07\x17\x35\x5d\x22\xe2\x8a\x04\x5b\x08\x34\xc8\xc3\xb1\x83\x65\x38\xd3\x82\x61\x45\xbb\x4a\x38\x84\x65\x11\x4d\x09\x97\xd3\x2d\x7a\x53\x5d\x30\xf0\x67\x18\xc5\x32\x60\x3e\x8f\x7b\xbc\x37\x48\x97\xd9\xe9\x92\xab\x2b\xd2\xda\x6c\x09\x1d\xf1\x60\x40\xc6\x8a\x8a\x98\xf0\x2c\x27\x52\xb5\xce\x81\x60\x96\x95\x98\xed\x4a\xd8\xf2\xf2\xd6\x9a\x64\x31\xb1\x1e\xfd\xa3\x67\x72\xf9\x7c\xce\xa3\x64\x69\x2f\x81\xf6\xe4\x96\x7f\x6d\xa8\x5b\x56\xbe\xa5\xee\xc6\x1a\x74\xe8\x06\xe4\xb3\xac\xa6\x9f\xe3\x4a\x02\xf2\x91\x0e\x5d\x89\x76\x44\xf3\x2e\xd5\x1c\xdf\x05\xd9\x7c\x1d\x94\x09\x7e\x5f\x86\x32\x87\x71\xd7\xa2\x0c\x30\x86\xe4\x61\x13\x45\xa2\x39\x17\x45\x0e\x27\xf7\x99\x9b\x5b\x2b\x51\xc0\xf4\xc3\xe8\x2c\x0a\x69\xf8\xfc\xb2\x82\x81\xdf\x84\x9a\x6a\x70\x73\x7c\xd7\xc8\x59\x96\x62\xe7\x78\x65\xf4\x1c\xdf\x06\x3f\xee\x15\x2c\xaf\x5a\xa1\xa8\x4c\xdc\xd2\xaf\xa5\x1b\xe3\x45\x6e\x6b\xe6\x5c\x94\xe2\x48\x34\xed\xa2\xc8\x11\xce\x7c\x18\xf2\x2c\x2f\xd8\xac\x6e\x29\xad\x6d\xb5\xc9\x33\xbe\x2f\x0b\xb7\x18\xab\x61\xb3\xf4\xd8\x88\x1e\xe5\x56\x6c\x7c\x31\x9d\x68\xc4\x31\xf1\xa1\xe2\x60\xe3\xc8\x1d\x49\x30\xa7\xfc\x75\x0f\xfb\x65\xc9\x5f\x02\x86\xd5\xa9\x6a\xf0\x60\xde\x39\x81\x42\x1b\x3d\x82\x35\xe5\xac\x90\x78\x5f\x4d\xf6\x48\xd9\x33\xdd\x87\xdd\x01\x3a\xcf\xe4\xd1\xaf\x82\x27\xe6\x70\x45\x25\xca\x9f\x6c\x9d\x9a\x72\x70\x7b\xf3\x82\xc9\xcb\xee\xe4\xf6\xf3\x38\x1a\x53\x26\x96\x6c\x93\x87\x50\xdd\x8a\x74\x5e\x33\x33\xf8\x08\x7e\x67\x13\xb4\x2a\xfa\x4b\xf5\x00\xce\x26\xa3\xce\x87\x16\x1f\xe0\x88\x13\x37\x60\x36\xe6\x9e\x3c\xee\x8a\x3d\xbc\x48\x05\x7c\x97\x3c\x94\x47\x4a\xdf\x0c\x58\x15\x71\xd1\xf0\xc9\xe3\x9e\x68\x7f\xb5\x29\xa8\x38\x92\xf3\xe1\x7b\xce\xe4\x77\x8a\xfd\x20\x1f\x47\x51\x15\xfe\x3d\x67\xf9\xdf\x10\xf3\x52\xa5\x03\xaa\x81\x66\xf8\x5f\x6d\x02\xb4\x6f\x9a\xb2\x19\xd8\xd7\xde\x6b\x4a\xa6\xa0\x94\xb7\x97\xa0\x5c\x55\xe8\x62\xdb\xe7\xbd\x66\x05\x69\xca\xc0\x5d\x6b\xf3\xa2\x45\x36\x88\x38\xe0\x00\xda\xf9\x6f\x65\x53\xf0\x78\xb3\x47\x70\x52\x99\xc3\x80\x2f\xd2\xee\x03\x1d\x34\x87\xd6\x77\xcf\x86\x81\x15\x3b\x74\x52\x1c\x38\xbc\xc0\x87\x65\x19\x4e\x29\x8e\xcc\xa1\x9b\xe4\xf6\x23\x4d\xe3\xa1\x9d\xe0\x40\x31\x09\x64\x68\x27\x60\x28\x25\x96\x0d\xed\x04\x17\xea\xd8\x01\x3b\xf6\xc2\xe1\x46\x75\x8a\xa7\x3e\x17\xf0\xd8\x0f\x89\x07\xab\x53\x3c\x70\x18\xdb\x28\xc9\x85\xf4\x4d\x8f\x9b\xe3\x96\x33\x27\x08\xa7\xb9\xb0\x82\xea\x87\xde\x75\x77\x2d\xef\x74\xcd\x9b\xa1\xd6\x70\xeb\x69\xaf\x65\xde\x28\xb5\x86\xdb\x60\xbe\x00\x0b\xa3\x35\xdc\xda\xea\xb5\xf0\xbd\x54\x6b\x68\x7e\x5e\x9f\xf6\xb6\x36\x7f\x67\x7f\x2e\x87\xdc\x30\xbe\xc2\x01\x51\x94\x14\x65\xfe\x87\xc4\xd5\x55\x94\x14\xdc\x35\x0b\xfb\xf1\x58\xfd\x3a\xd5\x89\x3b\xe8\xb7\xe5\xb9\x25\x4a\x0a\xee\xb7\x25\x4a\x8a\x27\x8f\x15\xd8\x53\x5d\xd1\xf6\x37\x4f\x4a\xea\x62\xf0\x35\x7e\x8c\xec\xa3\xe1\x57\x74\xc5\x05\xe0\xb6\x0d\xc2\x61\x52\xac\x68\x76\x61\x94\xa8\xb0\xb6\x80\xe6\x2a\x4a\xde\xc8\xb6\x22\x4a\x0a\x29\x2a\x3e\xbb\x91\x3f\x17\xde\xab\x7a\x1b\x88\xad\x46\x21\xec\xee\x8d\x20\xee\x8d\x20\xfe\xbc\x46\x10\x44\x5b\x41\x70\x51\xe9\x8e\x0c\x20\x1a\xd8\x35\xd8\xac\x9e\xdb\x2d\xa4\x60\x8d\xae\xdd\x76\xf4\x3d\x12\xea\xf9\x8c\x26\xea\xb1\x62\x8f\x1b\x7e\x33\x01\x5c\x79\x6f\x90\x92\xe5\xc0\x6b\x18\x61\xe9\xbe\xed\xb7\x89\xc0\x49\xa5\xfc\xc8\xff\xbf\xba\x22\xed\x36\xe2\xb3\xa9\x7c\xb6\xc0\x7f\xec\xa2\x77\x86\x51\x22\x5a\x6f\xec\xee\x63\x4a\x0b\x6c\xef\x0b\xd6\xe3\xed\x5c\xbe\x02\x05\x5e\xc2\x2a\x31\x4c\xdd\xb5\x7c\xcf\x2d\x5d\x4d\x29\x5a\xaa\x99\x74\xad\xb8\x32\xd2\x91\x7d\xec\x1a\xd6\xec\x80\x1e\x6c\xcd\x6e\x37\x52\x69\x87\x06\x26\xfe\xc6\xb1\x03\xdf\x3d\x36\x46\xc6\x38\xa3\x8c\x98\xe4\x7a\x30\x7d\xb2\x70\x72\x0f\xa3\xc9\x84\x82\x35\x32\x47\xb9\x75\x2e\x39\x57\x8f\x42\xf0\x71\x44\xa2\x44\xcc\x92\x34\x5c\x4e\xbc\x87\x10\xf3\xe8\xc2\xb6\x43\x5f\x3f\x82\x05\xe7\x30\xaa\x17\xe5\xa8\x3c\xf7\x3f\x98\x35\xe9\xae\xf4\x4a\x4f\x13\xa4\x22\xd5\x55\x30\x9a\xce\x47\x51\xe2\xba\xb7\x29\xd2\x29\x65\xdc\x9d\xd5\x40\xa7\x7d\xbe\xa8\x82\xc5\x82\x26\xb0\x96\x82\x84\x3f\x80\xb0\xb0\x2b\x6a\xab\xbb\x84\x11\x8c\x69\x16\x8d\x19\x7b\x92\xbd\xaa\x2f\x2c\x6e\x4f\xd3\x89\x80\x85\x7d\xa8\x12\xb5\x72\x78\x75\x7a\xbf\x2a\xb4\x2a\xbd\x05\xbf\x32\xd9\x25\xf5\xd8\x1d\x07\x71\x2c\xf0\x2b\xef\x70\xf8\x88\x66\x81\x5e\xba\x79\xf4\xab\xf0\x2c\x08\x77\x75\xb3\x20\xef\xb1\xff\x25\xa1\x81\xef\x5f\xcf\xa5\x1d\xc6\xb7\x32\x04\xf5\xeb\x4c\x2b\x51\xe3\x77\xcd\xe4\x5b\xb8\x62\x55\xac\xef\xed\x81\x74\x31\x89\x12\xeb\xa1\x52\x1d\x12\xb4\xcb\x22\x51\x95\xb8\x5e\xb6\x95\x06\x3c\x77\x3f\x7f\x5e\x7e\xf4\xe7\x1a\x5f\x57\x43\xd3\x60\x99\x19\xb5\x57\x0d\x7a\x1d\x46\xad\xdf\xff\x77\xc9\x33\xd2\x6e\x93\x61\x33\x6b\x2c\x84\x32\xaf\x4d\xd6\x0a\x78\x63\xbc\x9f\x2b\x27\x94\xcc\xe8\x7b\xeb\xa5\xf5\x17\x7e\x9c\xc9\xbd\x47\x5e\x09\x07\x98\xe1\x07\x73\x4c\x64\x40\xe2\x95\x58\xd4\x8d\x79\x51\x08\x4e\x95\x6c\xfc\xf9\x9c\x33\xa9\xe5\xb5\x4b\xf8\x95\x1f\x29\xa1\x3b\x31\x61\x9d\xd5\x51\x67\x6c\x6b\x25\xb8\x43\x9b\x92\x1f\x79\x32\x21\x90\x37\xf0\x0d\xb0\x48\xe7\x8b\xe2\x12\xab\x04\x1b\x6c\xa2\xb5\xab\xd0\xa4\x47\xc4\x9e\x86\x20\x7d\xac\x80\x1b\xe9\x6e\xaa\xd4\xd1\x94\x17\x13\x95\x03\x11\x55\xd6\x8d\xc1\xb8\x58\xd9\xf0\x88\x05\x37\x19\x87\x7e\x89\x57\xee\x1c\xea\x75\x94\x17\xce\xb3\xbf\x13\x63\x34\xa7\x1e\x8f\x50\x95\xa3\xd7\x35\xbb\xdb\x8b\x7a\x14\x24\xaf\xe9\x97\x8b\x90\x9b\xb5\x8a\x47\x70\x4a\x15\x59\xa4\x05\x7a\xe8\xca\x0b\x4b\xe1\x88\x3b\x1d\x22\xc6\xc3\x3e\xf5\x7e\x50\x80\x9a\x6f\x8a\x8c\xbd\x4d\xad\x47\xbe\x7d\x95\x2c\x48\xfb\xf6\xcb\xf6\x14\x62\x36\x4f\xf6\x70\x8f\x35\x2c\x1e\xc6\xc6\x9e\xab\xe8\x17\x4f\xb5\xdc\xe7\x59\x1c\x52\x8b\x40\x9d\x14\x3f\xb9\x55\x4f\xe6\x06\x03\x39\xdd\xf4\x8c\x66\x97\xc5\x0c\x1c\x91\xa0\x7a\x30\x76\x5c\xaf\x53\xd2\x1c\xcd\xc1\x8f\xf1\x4c\xd7\x7f\x43\xa1\x1c\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\xb4\xb7\xd5\x28\xd0\xde\x57\xd4\xc9\xc1\x6d\x74\xb5\x52\x2e\x63\x20\x25\x5a\x39\x69\x63\xc6\xfe\xe7\xaa\x32\xf8\xf5\x58\xff\x3c\x45\xc9\x3b\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\x4b\xbe\xe8\x56\x03\x45\x5d\xa3\x50\x5e\x5f\xeb\x65\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\x30\xe7\x69\xef\x6e\x9f\xca\xa9\x51\x36\xa0\xbb\xbb\x0f\x3d\x52\xf9\x78\xc7\xc3\x87\x91\x7f\xdb\x28\x6f\xee\xd8\x76\x4c\xb3\x22\x88\x12\xbf\x73\x5b\x07\x91\xfc\x36\xa9\x86\xa8\x39\x50\xdf\x4c\xaf\x26\x6b\x51\xc4\xca\xa8\x75\x05\x51\xd0\x6c\xce\x8e\xfc\xd1\x04\x6a\x36\xfb\x1d\x0a\x97\xb5\x64\x1a\x9d\xd1\x44\x9a\xb4\x98\x47\xea\x32\x5f\xb9\x96\xfd\x0b\x3f\x66\x6b\x73\x5b\xc0\x32\xaf\xdc\x69\xd7\x6f\x7c\x8b\x21\x9a\x2f\x11\xee\x99\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\x8a\x1f\xac\x20\xf4\x51\x34\x4a\x04\x0a\x0b\xd7\xe9\xb0\xfd\xd0\xbe\x11\x32\x5d\xad\xa4\xd5\x9c\xd6\xda\x96\xe0\xcd\xe3\x3f\xc0\x8f\xc1\xc1\x00\x54\xe1\xc1\x9c\xad\x0a\x70\x79\x28\xb4\x59\x6c\xbc\x8c\x13\x50\x7e\xc7\x10\x47\x9f\x29\x09\x48\x1e\x25\xd3\x98\x2a\x27\x5c\x00\xd9\x37\xec\xa1\x81\x82\xb9\x8f\x19\xee\x93\x83\xb7\x76\x75\x45\x4e\xda\x27\x5b\xa7\xed\xd3\xae\x12\x06\x6b\x7c\x00\x88\xee\x99\x78\x67\x5f\xd8\xaf\x61\x89\xe8\xce\x6d\xa0\x38\x2a\xc0\x56\x61\xab\x47\x1e\x81\x31\xf6\x26\xf4\x65\x0b\x7b\xa1\xd1\x1d\x72\x04\x59\xe9\xa5\xa1\x27\xfd\x3a\x94\x9d\x16\xa4\x37\x87\x87\x12\x50\x37\x30\x18\x90\x20\x8e\xc9\x28\xc8\xa3\x31\x77\x7e\x00\x2f\x05\x76\xb6\x85\x02\x27\x4e\xd9\xc9\x58\xf6\xa6\x47\x76\xb6\xeb\x8c\x4e\xcc\x85\x2d\x38\x9a\x3c\x81\x4b\x5d\x24\xa1\x53\x10\x20\x21\x22\xd4\xc9\x69\x8b\xec\xfd\x00\xeb\x53\xa7\x3d\xe6\x89\x95\xca\xb4\x7d\x59\xdb\xaa\x1c\x60\x46\x4b\x7b\x56\xb1\xda\x71\xab\xa5\x34\xab\x7d\x7e\x19\xde\x60\x1c\xa2\xdb\xb5\xb6\x51\x54\xe4\xc1\x03\x82\xbf\x4f\xd0\x6f\xe4\xff\xed\x54\xee\xba\x2a\x2c\xc6\x60\x7a\xa3\xb9\x11\xcb\xb7\x6a\x6a\xe4\x2c\x98\x73\x23\x26\xcc\x9c\x1a\xe4\x6e\xed\x96\x33\x63\xf5\xab\x62\x62\x50\x9b\x5f\x7b\x5e\xee\x72\x62\x4c\xbf\x27\x9a\x91\xa2\x99\x80\xb3\x51\x0b\x6c\x11\xb6\x39\xd2\xf9\x21\xa9\x25\x8c\x15\xb6\xc4\x54\x6c\x3d\x56\x80\xdb\xa7\x27\x3b\x02\x54\xa6\x71\x10\x05\xb1\x75\x6a\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\xaa\x98\x72\xb1\x49\xc8\x53\xb5\x6f\x9e\x61\xde\x7c\x53\xdd\x52\xc1\xf7\x9c\x09\x17\x9f\x2d\x63\xde\x8d\x8a\x4e\xcc\xca\xf1\x74\x6b\xd7\x6b\x8d\xe6\x59\x65\xf0\xa1\x88\xfc\xd2\xf9\x35\x5c\x28\x96\xee\xf6\xc2\x55\x51\x1c\xe4\x05\x39\x39\x65\xc2\x04\xaf\xf7\x46\xd3\xbe\xee\x9f\x77\x35\x07\x20\x67\x11\xc7\xc1\x12\x1c\x68\xf4\x33\x28\xf8\x54\x34\xd0\x84\x48\x2a\x8c\x63\xd1\x11\x46\x71\x60\xfb\xa6\x89\x8c\x2e\x49\x48\x27\xc1\x32\x06\x45\x68\xbe\x64\x72\xaa\xda\x98\x5b\xc2\x47\x4d\x4f\xc4\x78\xb4\x67\xd1\x38\x46\xdd\x80\x01\xeb\x1d\x71\x45\x51\xb8\xe1\xe9\xad\xd4\xa8\x5e\x3a\x6a\x97\x3a\x62\xb4\x44\x72\x7b\x8d\x00\xc5\x0b\x52\x3e\x69\x31\x8a\xef\x91\x16\x5b\x04\xec\xbf\xd3\xd6\xa9\xa6\x76\x01\x81\xd2\xa0\x50\xb2\x8c\xed\x67\x0f\x68\x36\x1b\xa1\xcd\xf6\x2e\x67\xf5\xb7\x66\x21\xb8\x1e\xaa\x9c\x95\xc0\xf7\x06\xe1\x29\x8f\xcf\x7a\x0e\x37\xbc\x6c\x38\xc6\x78\xd9\xbf\xb0\xea\x2d\x22\x16\xdc\xaa\xf3\xef\x13\x7e\x1a\xff\xf7\x69\xb7\x5e\x44\x10\xca\x5b\xe5\xea\xa1\xfc\xde\xc1\x8a\x61\x21\xa1\x9b\xb3\x0e\xf9\xf0\xd4\xbd\xcb\xb2\x70\xe6\xb9\xb4\x10\xf7\xe8\xf6\xc6\xe0\x75\x46\x6d\xde\xca\x08\x3f\xa8\xd2\x03\xaa\xcd\x16\x6a\x5c\xc1\x2a\xfb\x6f\x6c\x4c\xbc\x4b\x4a\xff\xfc\x5e\x51\x5d\xa7\xb2\x34\x9e\x60\x67\xb2\x82\x95\x39\x85\xd4\xb3\xe4\x93\x53\x9f\x07\xf1\xfe\x62\x99\xcf\x3a\x8e\x5b\x52\xf9\x4c\x5b\xfa\x18\x75\x6b\x66\x63\x71\x1d\xae\x9f\xf9\xbc\x7f\xe2\x96\x90\x13\xcf\xce\x59\x8f\x60\xe7\xb2\x96\x6f\xd2\x5b\x79\xf4\x15\x13\x88\x3d\xf9\xde\x7a\xfe\xa0\xeb\x8e\xd4\x21\x10\xff\xdb\xcf\x9f\xcf\x1d\x6b\x8d\x1b\xd6\xd2\x89\x60\xb3\x09\x7e\x52\x2b\xe6\x63\xe5\xd9\x58\x73\xee\x08\x2d\xdd\x91\xb1\x24\x91\x3b\xdb\x26\x0e\x41\xf9\xfd\xe8\x24\x4b\xe7\x5e\x73\x03\x0e\xe5\xe3\x2d\x23\xfb\xc1\x8e\x65\x20\x64\x58\x06\xad\xf0\x60\x4a\x32\x35\xde\x72\x03\x16\x25\x06\x82\x59\x94\xe1\x4c\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\x0d\xc6\x0f\x5d\xe9\xce\x59\x20\xaf\x6c\x11\x55\xd6\x89\x9b\x77\x2a\xf6\xad\x28\x28\xf0\xa1\xe0\x77\xec\xb8\xf4\x06\xd9\xe1\x1e\xef\xf9\x6e\x9b\x33\x90\x9c\x04\x93\x82\x66\x6a\x91\xe0\xfe\xde\x68\xad\xfa\xcb\xf8\x1c\x77\x6b\xce\x51\xe2\xb0\x9b\x54\x62\x4f\xc4\x8d\x79\x5b\x56\x3f\x76\xea\x51\xea\x43\xda\x0e\x78\x53\xc9\x68\x1a\x72\x1a\xf2\xb0\xba\x6f\x0c\x76\x63\xaf\x1a\xa6\x11\xa3\x32\xbd\xcd\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\xe8\x8c\x37\x25\x7d\x3b\x86\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\x91\x0d\xb3\x77\x5d\xee\x90\x89\x87\x11\xf3\x4c\x3d\x77\x3f\x6c\x5d\xba\x19\x0f\xb6\x2b\x9c\xd9\x1b\xb8\xb6\x2c\xbe\x0c\x2e\xb6\xb6\xe2\xd8\xf0\x9c\xab\x95\xb5\xdd\x35\xd5\xaa\xde\x8b\x44\xab\xeb\xb5\x17\xbc\xe5\x57\xbb\xea\x4d\xdc\xf5\x69\x6f\xeb\xf7\x8e\xbb\x7f\x5c\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\x63\xef\x2b\xb8\xe3\xd5\x9f\xc1\x1d\x37\x7d\x07\x77\xec\x3e\x84\x3b\xbe\x83\x97\x70\xcb\x5b\x3f\x85\x3b\x6e\xfc\x16\xee\xf7\x0e\xea\x7f\xdc\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\xf6\x1b\xa0\x1d\xdf\xc0\x02\xed\xb8\xb1\x09\xda\xb1\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\xd1\xf8\xa6\x29\xa1\xc9\x19\x09\x53\x0a\xd6\x0a\xf0\x3a\x30\x48\x42\x70\x60\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\x5d\x33\x22\x45\x96\x42\x9a\x09\x20\xa9\xff\x92\xcf\xd8\xee\x13\x4d\x93\x34\xa3\xc3\x38\x4a\xe8\xda\x35\xb7\x58\x65\x78\x68\xe4\xea\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\xc7\x45\x14\x43\x9d\x5c\x9f\x09\x6b\x87\x3f\x4f\x72\xc0\xa2\xe2\x52\xb1\x44\x5d\x64\x1c\x07\x79\x4e\x4e\xa0\xc8\xa9\xe8\x26\xcf\xd0\x4c\x98\x57\xb5\x36\x80\x7b\x23\x58\xa5\x42\xb9\xca\x38\x08\xa9\xf0\x64\xdd\xdc\xc9\x39\x40\xb2\x9a\x8e\xdf\x1e\x7e\xfc\xc0\xce\xd6\x30\x09\xed\x73\x1a\xb5\x39\x69\xb6\x3f\xa3\xdf\x6f\xd0\xef\x9f\xd0\xef\xfc\xd7\x60\x94\xca\x8f\x49\x94\x24\xf4\x52\x7d\xd1\x79\x91\xc2\x53\x46\x99\xb2\x88\xc6\x66\x42\x12\x24\x66\xc2\x3c\x1a\x67\x76\x4a\x1c\x47\x4e\x21\x03\xde\x00\x95\x1f\x46\x91\x69\x16\x24\xa1\x1a\x8a\x91\xf5\x93\xf1\xf5\xd1\xf8\x7a\x67\x7c\xbd\x34\xbe\xfe\xcf\xf8\xfa\x97\xf1\xf5\xd6\xf8\x7a\x61\x7c\xfd\xc3\xf8\x3a\xe6\x5f\x6b\xa7\xe5\xae\x6b\xd8\x1c\xbd\xdb\x7f\xc1\xa6\x78\x48\x76\xb6\x7b\x2a\xf1\xc3\xe1\x4f\x6f\xf7\x3f\x1e\xbf\x7f\xf9\xe9\xf5\xcb\xb7\x3f\x7d\x7c\x35\x24\x8f\x75\x26\xcc\xea\x50\xff\xd4\x39\x25\x94\x33\x24\x5f\x88\x95\xa0\x9d\xa8\x43\xc6\xa7\x17\x47\x3f\xbf\x25\xd7\xba\xa6\x77\x47\xaf\x5f\x33\xe8\x8f\x87\x6f\x5e\x1e\x1d\x7f\x1c\x92\xad\xcd\xcd\xcd\x81\xe8\xa1\xb8\xf1\x7e\x1e\xa7\xe3\xcf\x43\xd2\x66\xac\x33\x2f\xda\x46\xde\xfe\x18\xe2\x18\x0f\xf5\xdb\x46\xfe\x00\x83\xed\xe7\x75\xbe\x4f\xee\xe3\x60\xdc\x6f\x64\x7f\xf5\x8d\x6c\x4d\xb9\x80\xc8\x67\xc1\xce\x5d\x79\x80\x38\xc8\x2e\x17\x45\xfa\xf7\x0f\x78\x73\x18\x43\xda\x23\x1d\xfe\x82\x35\xe8\x05\x18\xb0\x9c\xb6\x37\xb4\x93\xeb\xbe\x01\x28\x2e\xc7\x0f\x54\x45\x12\x79\xf0\x40\xe6\xf6\xa5\xbf\x08\x2e\x26\xcf\xe8\x45\xdb\x7e\x45\x67\x78\xfe\xfa\x81\x6c\xb3\xd2\xb6\xeb\xe3\x6d\xe9\x2e\xd2\x2c\x4e\xe4\x65\xb8\xba\xe0\xb7\x9c\xb3\x13\xeb\xb5\x1d\x07\x95\x38\x62\x9d\xeb\xbf\xa2\x17\x7d\xd0\x5e\x0a\xcf\xbd\x3e\x1b\x23\x86\x15\x39\x6c\xdd\x3a\x3f\xd1\x71\xf5\xdb\x90\x6c\x7f\xf3\x84\x97\x44\x8f\x93\xe5\x9b\x33\xc6\xf2\x14\x8e\x5b\xc3\x6f\xbe\xeb\xb5\x4c\x94\xb7\x86\x4f\x37\xaf\x4f\x7b\xdb\x8d\x7c\x3e\xdd\xf3\xbd\x7b\xbe\xf7\xe7\xe5\x7b\x9a\xed\xf1\x77\xfe\x77\xc0\xf7\x2c\xd9\x7d\x75\xd1\xdd\x23\xb9\xcb\x82\x3e\xc1\x7d\xa5\x50\x43\x36\xaf\xed\x0f\x04\xbb\xd7\xb1\x88\x26\x4f\x31\x00\xfb\x56\x22\xfc\x32\x89\x8a\x37\xc1\x42\x89\x8b\x6d\x29\x51\x0f\x39\x0f\x6a\x6f\x4a\x59\x93\x49\xed\x43\xcd\x16\xdb\x5b\x86\x9c\x3f\x44\x19\x9b\x9b\xaa\xd0\xff\x56\xe4\x8d\x82\xd1\x28\x98\x52\xd5\x12\xce\x43\xc2\xff\xd0\xce\x9b\x7b\xea\x44\xd9\x6f\xaa\xb3\xe3\xf4\x8c\xc6\xc1\x58\x36\x6b\x67\xeb\x33\xc6\xd0\x97\x3d\xf5\x57\x8e\x20\x7e\xaa\x85\xc8\x67\x41\x92\xa4\x89\x31\x6e\x13\x42\x9f\x6b\x86\x15\x10\x35\xad\xc0\xc9\x6a\xe8\x81\xc0\xa8\xd4\xe7\xa5\x61\x35\x50\x5d\x4d\xe2\xec\x36\xf4\x02\x19\x95\xa9\xf3\x98\x3d\x36\x0f\xa0\x7f\x88\x26\xa0\x41\xae\x1e\x38\x04\xfa\xd9\x84\xf5\x81\xe2\xb9\x86\x53\x5f\x65\xc5\xb8\xbf\x8d\xea\xc6\xd5\x37\x2d\x80\xca\x14\x2b\x94\x61\xc5\xfc\xc6\x56\xda\x11\xc3\x22\x08\x85\x29\x29\x98\x7a\x5e\x2c\xe8\x98\x6d\x5e\xca\x3c\x1f\x1b\x5d\x09\xef\x29\x3e\xcb\x29\x5d\xc5\x88\x32\xb8\x50\x84\xe3\xb2\x6c\xb0\xc6\xb3\x20\x0b\xc6\x05\xcd\x72\xa9\xe2\x87\x7b\x79\x51\x1a\xed\x23\xde\x36\xa2\x69\xd2\x43\xb6\xd0\x64\x73\xcd\xef\xf6\x23\x9a\xce\x0a\x22\x3d\xd2\x5a\xde\x7d\xc5\x18\x0c\x69\x93\x83\xf4\xa0\x77\x79\x0f\xda\xf1\xf8\x18\xe2\x16\x22\x00\x03\x11\x69\xe1\xb5\xaa\xba\x21\xde\xea\xf6\x7f\x49\xa3\x04\x82\x35\x90\x67\x50\x07\x19\x92\xd6\x66\xab\x4b\x36\x04\x70\x89\xe1\xdb\x8d\xe7\x02\xa2\xf5\xfc\xd9\x27\x03\x06\xb1\xe2\x6c\x88\x1e\x6e\x70\x8f\xcb\x37\x9d\x97\x32\x43\x44\xd3\x11\x0d\x6c\x9d\x60\x86\x08\x91\x3c\x5c\x1f\xd3\xd6\xbc\x70\x6f\xcd\x15\xb3\x12\x25\xac\x12\x3f\xb2\xb0\x3f\x6a\x8f\xa3\x24\xd6\xb8\x36\x3b\xe4\x1e\x48\x8e\xf9\xd6\xae\x44\xfa\x19\x0f\xf6\x3c\x18\x90\x1f\xa3\x24\x24\xfc\x71\x97\xe8\xa8\x0a\xd6\xcc\x24\x8a\x56\x4b\xdf\xe4\x83\xed\x4b\x0f\xe2\x47\xcd\xe8\x85\x34\x61\x56\x67\x2e\x96\xc6\x4f\x3d\xec\xc4\x51\x7e\x56\x62\xd5\x6c\xe3\x77\x2f\x60\x5c\x23\x6c\x6a\x76\x49\xb4\xb1\xb7\x8d\xc1\x65\x20\x64\x6c\xdb\xa1\x9b\xea\x44\xac\x1d\x11\xfa\x42\xb5\x30\x21\x1d\x5e\x64\x6f\x8f\x6c\x76\x8d\x53\xda\x28\xa3\xc1\x67\x0d\xca\x46\xb9\xb1\x47\xc4\xab\x72\x36\x83\x07\xb3\x20\x3b\x48\x43\x0a\x35\x78\x0f\x61\x6c\xb2\xa5\x39\x4e\x5e\x64\xcd\x28\x84\x4f\xda\x4a\x24\xb2\xcf\x8a\xfc\x76\x34\x02\xcd\xfd\xf7\x10\xc9\x4d\x66\x3e\x2f\xca\x5e\xa7\x9b\x93\xed\xf1\x31\xdf\x59\x64\x74\x12\x5d\xf0\x08\x5a\x9b\x17\x5d\x36\x0b\xc0\x35\xfc\xee\xed\x45\xa8\xb7\xf2\xd9\xf7\xda\x2e\xc3\x11\x34\x88\x81\x9b\x57\x06\x13\xf0\x85\xf8\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\x70\xf8\x68\x46\x19\xd4\x58\xcc\xcd\x7e\xd1\x89\x6e\xb6\xd2\xc1\x3a\x51\x44\x06\x37\xbc\xb6\x69\x10\xd6\xdf\x8d\x3d\x92\xc8\x7d\xe1\x7b\xb2\x4d\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\x8c\x22\x9b\xc7\x4b\xb7\x89\x1f\xd3\x1f\x97\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\x34\xe9\xb4\x3b\xed\x2e\xf8\x86\x7c\xb4\xe5\x79\x0e\xa9\xe0\x1d\x3b\xc1\xe2\x72\x41\x45\x73\x00\x04\x54\x64\xfa\x33\xeb\x44\xdd\x2f\x32\x7e\x70\x9f\xc1\xef\x92\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\x60\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\xd1\x8b\xa3\x21\x99\x07\x9f\x41\x35\x1c\x25\x67\xcb\x38\xa1\x59\x30\x8a\xe9\x5d\x0e\x50\x9f\x06\xec\xd7\xbb\x5b\xe4\x11\x41\xd9\xdd\x6e\x3f\xa3\x8b\x38\x18\xd3\x4e\x9b\xb4\xc1\xa9\x1b\x3b\x2d\xb4\xcc\x08\x91\x69\x72\x46\xb3\x22\xd7\xf1\x36\x41\xee\x0b\xe9\x38\x9a\x07\xb1\xcd\x64\xa3\xc4\xcf\xec\x8b\xf4\x05\x2f\xe0\x52\x5e\x65\xec\x4c\xd3\xad\x21\x17\xf0\x44\x4d\xb5\xd1\x1f\x8b\xd4\x0d\x8e\xa9\xc2\xcf\x34\x19\x63\xad\x6c\xcb\x78\xe2\x5d\x8d\x0b\xd5\x55\x1d\x99\x35\x91\x5a\x52\x77\x7c\x9e\xb8\xdc\x42\x7d\x6a\xee\x28\xc6\x61\x9f\x03\xc4\x34\xcf\x3f\xce\x82\xa4\xb3\x09\x4e\x64\x1f\x71\xab\x73\x61\xbd\x2f\x08\x6b\xab\x0b\xb1\x5b\x51\x8e\x81\xc5\xfd\x25\xb8\x69\x16\xa8\x0c\x92\x4b\xe1\x78\x47\xb8\x23\x4d\xca\xd1\xda\x17\x78\xdd\x4f\x42\xae\xfe\xe7\x34\x14\x4d\x2e\x73\xe1\x48\x3d\x27\x23\x3a\x49\x33\xda\x77\xe8\xea\x95\x38\x3a\x54\xe3\xfe\x4a\xec\x41\x35\xa4\xf5\x0a\xf6\x79\x03\xf9\x6a\xfd\x3e\x14\xa6\x62\xf3\xe0\x82\x87\xad\xbc\x88\x8a\xcb\x21\x79\x0a\x2a\x6c\xb9\xeb\x44\xb9\x70\x69\x0c\x45\xbb\xf6\x26\x83\x26\xb9\xb3\xc1\x20\x76\x8d\xa2\x78\x3a\xab\x0b\x5b\x65\x85\x21\xdd\x19\xa3\x1d\x76\x0a\xe1\x48\x6b\x7b\xab\x80\xf8\x4a\x7f\xff\x70\xf4\xb6\xaf\xb0\xcc\xdb\xd3\x0e\x2c\xc1\x75\x6c\x4e\x02\x3b\x94\x67\x8f\x2c\x82\x3c\x67\xbc\xab\x98\x65\xe9\x72\x3a\x33\x57\x80\x1a\x88\xa0\x35\xa8\xd5\xbd\x9c\xd4\x5c\xed\x11\x9c\x96\x3c\x32\x6f\xe9\x88\x25\x80\x78\xdb\x61\x56\x57\x53\xdb\x99\xb4\x1f\x45\x15\x90\xce\x7a\x94\xff\x18\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\x08\x57\x7c\x34\x39\x4e\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\xc3\x73\xb5\x6e\xcc\x5e\x1b\x0b\x88\x15\x94\x24\xb0\xe4\x99\xf0\xdf\x33\xf8\xaf\x5f\xa4\xaf\xd3\x73\x9a\x1d\x04\x39\xed\x00\x73\xe1\xfa\x5e\xcd\xc7\x18\xd4\x3f\xc4\x2d\xb3\xb8\xba\x39\x61\xff\x9f\xea\xa3\x38\x02\xc1\x7e\xbf\x31\xe1\x71\x4f\x64\x09\x3d\x27\x2f\xd9\xa8\x3a\x6d\xb8\xea\x85\x8e\x80\xad\xea\xbf\xdb\x05\xa1\x17\x51\x5e\xe4\x3d\xb2\x88\x69\x90\x83\x58\x0c\x23\x4f\x13\x85\xaa\x49\x1a\xc7\xe9\x79\x94\x4c\xa1\x64\xce\xb8\xa0\xb5\x8c\x44\x0f\x7b\xe0\x5f\xa1\xa7\x9f\x7d\x54\x44\x89\x55\xbd\x07\xef\x57\xa6\x57\xe1\xe0\x33\x85\x45\xc8\x19\x3e\x5c\x46\x47\x60\x4f\xab\x98\x2c\x27\x01\xc6\x6a\xc1\x57\x05\x9f\x78\x8e\x5a\x41\x59\xef\xd2\x3c\x8f\x46\x31\x9f\x42\x70\xa1\x21\x8c\xfa\x3e\x1c\x32\xf9\x32\x2b\xf8\x4f\x26\x52\x4b\x6c\xbd\x9c\x4c\xa2\xe9\xa5\xf8\x38\x92\xa4\xf4\x88\x7c\x66\xcd\xf3\x3f\x7d\x5d\x05\x9f\xe2\x66\x8b\x83\xcd\x35\x98\xba\x5c\xe2\x9f\xf2\x2a\x8a\xc3\x4d\x35\x9c\xba\xff\xe1\x9f\xe2\xc2\x48\xe7\xf1\x02\x8f\x1e\xa9\x85\xa9\xef\x71\x78\x81\x5f\x83\x51\x6a\xe4\x79\x4a\xc8\x7b\x18\x3e\x00\xb8\xbe\xc1\x79\xbc\x04\xea\x05\x2a\xcc\x3f\x05\x16\x10\x08\xb1\x20\xd0\x07\x5c\xa6\x08\x84\x50\x8d\xc3\x29\xfa\x5d\xc8\xdf\xb6\x48\xc1\xf9\x82\x75\xf2\xbd\x52\x72\x3a\x27\x87\x71\x90\xb0\x93\x41\xa0\x58\xb3\x48\x17\xba\xb2\x34\x23\x01\x79\xf5\xf2\x9f\x70\x08\x97\xd2\xda\x9d\x31\x14\xb5\xcf\xca\xa3\xdd\xcf\x33\x2a\xfd\xec\x05\xe8\x2a\x57\x44\x41\x41\xc1\x02\xd8\x7a\x0a\x72\x72\x4e\xd9\x02\xd1\x0e\x56\xe4\x30\xd6\x90\x34\xf4\x33\x35\x8e\xe4\x72\x9c\x98\xa5\x70\x51\x87\xd5\x2c\x99\x04\x16\x8a\x78\x09\x1c\x35\xd6\xe4\x54\x9c\x3b\x59\xf2\x10\xde\x86\x45\x05\xe4\x99\xd1\xc8\x10\x7f\x21\xc9\xaa\x76\xf9\x06\x1c\xc7\x9e\x15\x7c\x4e\xa3\xfb\x05\xfb\xdf\xb2\xc4\x8b\xb4\x6a\x81\xa3\xf3\xc2\x6f\xb6\xd4\xd9\x6a\xfb\x1d\x17\x3b\x20\xe4\x6e\x96\x7a\x11\xcd\x69\xfe\x7b\x2c\xf3\x44\x28\x17\xd9\xe2\x56\xaa\xaa\x9c\x1f\xf3\xd9\x16\x4d\x94\x29\x8b\x43\x0d\xaa\x23\x8d\x68\x42\x53\x81\xbc\x3a\x64\x53\xaf\x49\xc1\xac\x4d\x39\xb9\xd2\x15\x68\x00\x85\x7e\x6c\x7b\x63\x4d\x42\xcd\xf1\xe7\x1b\x26\x03\xc2\xaa\x97\xe5\xc5\x8f\xab\x2b\xb2\xb9\xeb\x3d\xdb\x88\x7a\x9d\xb3\x09\x4f\x37\x0e\x44\x02\xe5\xb2\x27\x0f\x1e\x10\xf1\xdb\x27\xf3\xb3\x26\xed\x5c\x7c\xc0\xf0\xb9\x40\x33\x44\x31\x51\x58\xa9\x44\x36\x2f\xda\xbd\x76\x1b\xdf\xb7\x58\x8e\xd2\x7c\xa5\x31\x9d\x94\x8a\x74\x89\x0c\x1d\xeb\xa1\x14\x45\x27\x1c\x4c\x06\xf1\x50\x27\x31\x61\x35\x09\xb0\xc5\x79\xda\xce\xc9\x58\x85\x74\x71\x48\xcb\x8c\xf8\xd2\x84\xbe\x4a\xa8\x06\x9d\x91\xcd\x3a\x4d\x7d\x97\x41\x32\x0c\x7c\x84\x28\xcb\xb7\x5e\xe1\xc5\x77\x07\x39\xad\x53\x05\xb0\x46\xa2\x76\xea\x5a\x93\x5b\xfe\xb5\x60\x96\xfb\x8b\x78\x99\xeb\x2e\x88\x6f\xaf\x77\x43\x05\x64\x2a\x92\x66\x74\xfc\x39\x97\xa7\x26\xce\x22\xe5\x2d\x67\x2e\xde\xca\xc5\x97\xe0\xc6\xd7\x1b\x8c\x98\x93\xfc\xd8\x1b\x88\xd8\x0c\x29\x8c\x1a\x60\xeb\x3f\x40\x05\xb0\x63\x3b\x08\xae\x24\xa6\xce\xaa\xdc\x98\x39\x51\xde\xd2\xa0\x0d\xfe\xb3\x79\x71\xb2\xf9\xe8\xbb\xe0\xd1\xe4\xf4\xcb\xe3\xcd\xeb\xff\x19\x44\xfd\x82\xe6\x85\x02\x5f\x61\xf0\x15\x63\xfe\x4a\xa3\x6d\x30\x4e\x50\x00\x0c\xfe\xd3\xd9\xbc\xe8\x3e\xab\x1c\x28\xa6\xc0\xc1\x40\x07\xcb\xe2\xe1\xb0\xa0\x7b\xdc\x85\xb0\xb0\x3a\x9c\xc3\x43\x5e\xb6\x21\xa3\x61\x9b\x14\x2c\x3c\x01\x12\xd3\x57\x85\xb7\x33\x66\x5f\x18\xa3\x43\x60\xfb\x8f\x7e\xf4\x82\x59\x5d\x86\xd8\x5d\xed\x1c\xbc\x1d\xe7\x73\xf6\xef\x38\x58\xe4\x20\x3c\x88\xdf\x3d\xec\x9e\xd1\xee\x2d\xf7\x3a\x8f\x3a\x6b\x54\x7e\xa4\xf6\x76\x8e\x19\x1a\x8c\x67\x64\x1c\xe4\x4e\x35\x51\xce\xa9\x64\x39\x17\xb3\x83\x48\x89\xaf\xb1\xe6\x04\xc5\xdb\xca\x97\xf3\x39\x0d\x4b\x69\xcb\x6a\xee\xae\x69\xcc\xaa\xbe\x8a\xd6\x06\x03\x3e\x20\x0b\x39\x81\x2a\x29\x7e\x39\x1b\x90\xd6\x86\x08\x88\x57\x41\x0e\xae\x68\x66\xc1\x8e\x6c\xc4\xd4\xa4\x48\x59\xc7\xe7\xee\xe5\xf1\x26\xdc\x50\x12\x8b\x3c\xc0\x75\x77\x31\x23\x31\x85\xc7\xd4\x28\xfe\xde\x62\x41\x33\xd6\x5b\x39\x0f\x09\xc4\x2e\x9c\x46\x3c\xbc\x5d\x90\xd3\x79\xb0\x60\xf3\xb1\x65\xe8\xf9\x3a\xca\x7e\x01\x75\x1a\x9c\xb2\x6d\x3d\xe9\x92\x1f\xc8\xb7\x6c\x37\x17\x59\x27\xd1\x69\xbf\x48\x8f\x59\x43\x42\x13\xb4\xbe\xb7\x87\x32\x81\xe2\xab\x2b\xfc\x7e\xcf\x53\x23\xd6\x2d\x59\x35\x96\x78\x0a\x47\x6b\x52\x73\x7c\x83\xef\xeb\xe8\x0b\x8a\x4c\xdf\x88\x83\x9e\x24\xc7\x12\x5a\x2c\xd2\x3b\xa5\x45\xa9\xbc\x56\xfb\xf2\x0a\xa4\x88\x54\xc6\x8a\xfc\xec\x47\xd7\xa2\x9d\x76\x5b\xd0\x92\x4b\xa7\x06\x82\x6f\x44\xb5\x08\x68\xec\xf4\x9e\x55\x54\x41\xc7\xb2\x17\xe8\xd6\xdd\xa6\x69\x60\x79\x33\x6d\xf9\xc7\xa8\xf4\x3b\x76\xee\x99\x70\xff\xf9\xf2\x22\x4e\x91\xb8\x41\xc1\x75\x04\x6c\x92\x90\xdd\xff\x8d\xbd\x52\xea\x46\xf4\x65\xb3\xd2\xda\x9a\x2a\x69\xd3\x2a\x69\x4a\x9e\x5a\xd2\x34\x18\x69\x91\x32\x89\x32\x0a\xc9\xf6\x26\x77\x19\xf4\x48\xdc\x0f\xf2\x36\xf9\xf3\x84\xcd\x0b\xc2\x6d\x3b\x5c\xdb\xae\x5a\x52\xf6\x5f\xf6\x0b\xe7\x03\x98\x6f\x2b\xfb\xad\x66\xf4\x6b\x49\x33\xde\x6d\x4f\xfa\xd4\x95\xf8\x40\x32\x3c\xdf\x6b\xab\xb6\x59\x4f\x45\xe2\xee\xcb\x57\x9f\x09\x21\x23\x2f\xc2\x8d\x92\xaa\x51\x3f\xa6\xea\x91\xc7\x9b\xfe\x4b\x02\xe9\x87\x58\x1e\xa6\x73\x2d\xe5\xd6\xc7\xd8\xf4\x9e\x24\x7d\x37\x5f\x46\xdc\x4d\xbe\x93\xf9\xce\x80\xa4\xc3\xbb\x61\x89\x85\xb2\x6f\x49\x5e\x04\xc9\x98\x71\x11\x5d\xf8\xea\x4a\x21\x4d\x14\x86\xc7\x6b\xf0\xcb\xf0\x9b\xe1\x4d\xe5\xa6\x11\xc0\x8b\x54\x95\xed\xa6\x88\x92\xe7\xe1\x3a\x2c\x7d\x70\x6c\x8b\x1a\xa2\xc8\x13\x21\xc9\x8b\x1f\xc1\x5a\x45\xcf\x60\x34\xbc\x6f\xed\xbb\x43\x0f\xef\x4b\x63\xdc\xc8\x1e\xd7\x63\xe7\x47\x6d\x43\xb2\x2a\x7e\x64\xd1\x1b\x61\x48\x96\x68\x37\x1c\x11\xeb\x53\x51\x3f\x1c\xde\xf5\x1b\x0c\xe6\x48\xf4\xad\xe1\x62\x60\xf2\x45\xb2\x8c\x63\x08\x92\xd0\x71\x57\x08\xd8\x6d\x83\x0a\xc3\x33\x76\x71\x5d\xdb\x70\xe4\x23\xde\xd9\x06\xec\x80\x03\xde\x84\x19\xf0\xa4\x1b\x4d\xa4\xe8\x5e\xd3\xd1\x80\x07\xc0\xfa\xb1\x38\x01\x35\x1a\x8e\xc4\x0d\x8a\xd1\x90\xa5\x41\xc1\xca\x31\xd8\x07\x12\xbe\x8f\x82\x89\x5c\x2a\xa9\xce\x1c\xc4\xdf\x73\x73\x5d\x69\x03\x84\xca\x31\xb0\x62\xf6\xa3\x01\xe5\x39\x29\xbb\x74\xf7\xa9\xf5\x75\xb8\x98\xe4\xaf\x70\xb5\x2d\xeb\x35\x19\x43\xd4\xa7\x0e\xf5\xec\x6d\xf8\x38\xba\xca\xa8\x03\x31\xee\x97\x6c\x02\xe9\x72\x4e\x46\x71\x3a\xfe\x4c\x66\x34\x08\x69\xc6\x3e\xd2\xb9\x6d\xb4\x11\xe5\xcf\x59\xb2\x4f\x68\x98\xd1\x0b\xe5\x16\x1d\xca\x92\x49\x14\x17\xb6\x32\xd3\x43\xb0\x00\x6b\x78\x1f\x66\x29\x95\xe7\xfc\x6f\xb6\xb6\xf5\x41\x9f\x83\xd7\xe0\xa5\xfc\x98\xce\xeb\xc2\x55\xf9\x4e\xe9\x2e\x94\x2f\xe0\xb0\x3e\x69\xaf\xb9\xfd\xb8\xc1\xcc\xc4\x29\x13\xf3\x16\xd1\xd8\x9d\x87\x8f\x2c\xb9\x6e\x1e\x0a\x05\x54\x31\x01\x50\x93\x31\x01\x50\xac\x72\x02\x9e\x3c\xd6\xf8\xe7\xd0\x37\xc6\x3f\x54\x85\x6b\xf2\xa1\xdf\x01\xba\x11\xf6\x4b\xfc\x8e\x08\x91\x6f\x28\x7f\xf4\x64\x2a\xbc\xf9\x19\xaa\x5f\x3c\x1d\x04\xc3\x21\xff\x4f\xa6\x08\x03\x92\xa1\xfe\xc9\x73\x90\x71\xc9\x10\x7f\xc8\x72\xc7\xc5\xe4\xe9\x50\xfc\x2f\xd3\xc0\x5c\x65\x28\x7f\xe8\x7a\x38\xac\xfc\xa5\xd3\x05\xbc\xfa\x29\xea\x71\x6d\x6e\x87\xbe\x44\x0e\xed\x9a\x72\x0e\x3d\x69\x06\xac\xb4\x9a\x1c\xda\x09\x72\x1c\x3f\x53\x18\xc5\xcf\x14\x8d\x01\xd2\xc4\x0f\x09\xa7\xa4\xc5\x21\xfe\x90\xb9\xa6\xca\x7a\xe8\xa4\x28\xac\x71\x41\x7d\xa8\x7f\xf2\x1c\x24\x1d\x0f\xf1\x87\xcc\x35\x4e\x22\x43\x3b\x41\x42\xa1\x7c\x2b\xc7\x3a\xba\x0f\xdd\x24\xd9\x43\x07\xd2\x49\x92\x75\x4a\x61\x6c\x88\x7e\xe3\xfe\x26\xd3\xa1\xfa\x25\xd3\xf9\x9e\x3a\x54\xbf\xd4\xe8\xf9\x7a\x1f\xea\x9f\x6a\x4c\x6c\x97\x1c\xca\x1f\x32\x95\x6d\x58\x43\xf1\xbf\xaa\x83\xf1\xbb\xa1\xfc\x21\x53\x81\x6d\x0c\xe5\x8f\x1e\x2c\x30\xee\x9f\x4e\x3c\xea\x6e\x0d\xb7\xbe\xeb\x55\xba\xb7\xe9\xb5\x96\xc5\xe4\x69\x6b\xf8\xf4\x9b\xeb\xd3\xde\xf6\x56\x13\x87\x0f\xe6\x12\xde\xe3\x0b\xb8\x25\xfc\x1c\xb4\x86\xa4\xb5\xd9\xdf\xde\xec\x6f\xb5\xd6\xae\xa5\x27\xb8\xed\x46\x81\x8a\xef\x1d\x49\xdc\x3b\x92\xf8\x2b\x38\x92\x10\xb5\xac\xb9\xae\xe0\xfe\x4e\x27\x93\x8c\x5e\x92\x9f\xa3\x78\xfc\x99\x92\xef\x7f\xa1\x93\x89\xed\x4d\xa2\xa1\xc3\x38\x00\x8b\x82\x84\x1c\x31\x89\x3b\x00\xa8\x28\x48\x5c\xb0\x1f\x83\x11\x03\xfb\x47\x3a\xa5\x71\x5e\xd0\x38\xa6\x19\xf9\x7e\x02\x89\x2e\xf0\x4f\xc1\x19\xf9\x39\x4d\x43\xf2\xfd\xb4\xd4\xcb\xc5\x63\xed\xdd\x47\xb8\x82\x7c\x13\x24\xc1\xd4\x74\x3d\xd1\x1f\x30\x2c\x0c\x32\x0e\x30\xe7\x00\xd2\xc5\xc4\xe1\x08\x0e\x47\x36\x70\x34\x0a\x12\x09\xf2\x12\xac\xf8\x6d\x08\x2e\x79\xe5\x03\x5a\xcc\x24\xe0\x8b\xe7\x15\x70\xe1\x48\xb9\x9b\x9d\x55\xd5\x97\xcf\x54\x7d\x6f\xc1\x31\x79\x19\x60\x42\x0b\x09\xf8\x8e\x66\x39\xbc\xa4\x2a\x87\x5e\x08\x10\xd5\x89\xf3\x20\x9b\x57\x75\x83\xe5\x2b\x60\x5a\x14\x10\xb4\xc9\x85\xcf\x45\x96\x04\x95\x5c\xc5\x80\x94\xec\x82\x9d\xa8\xb4\x6f\x8f\x28\xb6\x2a\x44\x51\xe5\xcb\x5d\x84\x70\x20\xe9\x8c\x49\xbc\xdb\xa0\x49\xe8\xe9\x1b\xcf\x90\x60\xcf\xe1\xc4\xe4\x42\x8d\x58\xba\xc2\x64\x96\x2e\x68\x56\x5c\x7a\xe0\x16\x22\x4b\x82\xbe\x2a\x8a\xc5\xbb\x2c\x3d\x8b\x42\x2f\xb9\xb1\x85\xba\x10\xd9\x8a\xd8\x16\xe3\x8a\x12\xd1\x62\x6c\x17\x68\xe6\xd0\x70\x6d\x4d\xc9\xea\x3f\xd3\xd1\x0e\xe9\xc8\x6a\x4c\xa7\xbc\x99\xbd\x42\x12\x7a\x6e\x2d\x1b\x5d\x12\xf9\xe7\x15\x91\x56\x51\xcf\x25\x14\x02\xa2\xfc\xa9\x0b\x3d\x67\xcb\x05\xfc\xf4\xe3\x2a\xc2\x91\xc8\x7c\xf1\xdc\xc9\xcb\x67\xb2\xe4\x87\x99\x5b\x32\x81\x35\xc0\x72\xdf\xd2\xc2\xc9\x5d\x68\xc2\x67\x20\x72\x1d\x38\x70\xa3\x5f\x7f\x95\x6d\x30\xba\x76\xfb\xa0\x09\x1c\x80\xc4\x67\x07\xc3\x68\xca\xd6\x47\x8d\x60\x11\x0d\xd5\x66\x28\xfe\xe7\x47\x0e\xdc\x49\x81\xad\xdc\x28\x8a\xc9\x67\x68\x7c\xf5\x14\x0c\xa2\x97\x21\xfe\x70\x9a\xf8\xa4\xd6\x00\xff\xe1\x0c\x50\x00\x74\x74\xfb\x82\x9c\x23\x9a\x0f\xd1\xef\x0e\x37\xe6\xb9\xee\xee\x32\x89\x69\x30\x00\x0f\xbc\x39\x25\x7a\x0c\x29\xdf\x89\xc1\x25\xd0\x1a\x23\x37\xcf\xf8\xea\xc6\x56\x3a\x2e\x26\x34\xca\x3a\x65\x38\x4d\x8a\x29\x0f\x87\x0c\xae\xa7\x71\x5c\x78\x65\xd2\xf6\xf4\x25\xa3\x3c\x56\x84\xee\xc5\x67\x4a\x17\x87\xf9\x87\xcb\x64\x1c\x25\xd3\xca\xae\x40\x59\x0b\xbe\x19\x05\x7a\x3a\x82\xf9\xc2\x73\x6d\xbf\x62\x41\xc9\x57\x30\xdc\x9b\x14\x7c\x79\x60\xe4\x8b\x59\x09\x05\xdf\x1e\x38\xf1\xec\x5a\x82\xb1\x4f\x07\x0a\xbf\xc0\xe5\x80\x2a\xc5\x0b\x6b\xd4\x29\x13\x3c\x6d\xeb\xe7\x54\xb2\x79\x91\xe2\xad\xd5\x86\x46\x69\x9e\xba\x31\x2e\x65\xed\x55\x38\xe5\x16\x8e\x12\xf2\x67\xea\x1f\x19\x86\x12\xdf\x0e\x1c\x36\x6c\xe1\x90\x2a\xc5\x03\xeb\xde\x0a\xcb\x32\x07\xf6\x6d\xa1\xd3\xe7\xb2\xb2\x4e\x8e\xa7\xdd\xc3\xe7\xfb\x6f\x51\x63\xec\xd3\x81\xd2\xde\x69\x38\x98\xf8\xf6\xc1\x49\xc7\x29\x0a\x10\x12\xd8\x2e\x66\x2f\x7c\xbe\xf5\xe3\x87\xdc\xfc\x52\xc8\x74\xae\x68\x5e\xd7\xc1\x9d\xb4\x0d\x59\x76\x7d\x1a\x46\x19\xa8\x8a\xc7\xc1\x02\x1e\x5f\xa0\x0b\x4c\xcf\x8c\x1e\x1e\xec\xbf\x33\xd6\x3e\x2b\x87\x2d\xe4\x22\x2e\x4a\xb2\xe5\xcb\xa4\x4a\x9e\x6f\xbc\xf5\x64\x10\x7d\xd1\x8c\x5c\xd9\xe0\x4f\x46\xf1\xdf\xaa\x80\xa3\x27\x8a\x77\xc3\x5e\x27\xc4\x91\x8e\x79\xe7\x9c\x80\x0e\xa6\x2d\xf7\xa4\x24\x0d\x69\xbb\x67\x40\x4c\xc1\x2e\x64\x48\xda\x4c\xe8\xf8\x34\x8e\x23\x9a\x14\xff\xe0\xe0\x6d\x7d\x27\xdd\xed\xdd\xa4\x35\x5a\x9c\xa7\xd9\xe7\xb2\x06\x13\x5a\x7c\x12\xa0\x16\x88\x19\x2f\x60\x68\xaf\xf2\x5b\x76\x8b\x0a\x85\x76\x59\xbf\x68\x31\xfb\x04\x73\x3d\x4e\xe3\x7f\xfc\x0e\xfd\x3b\x9f\x45\xf9\x42\xb9\x46\x76\xba\x97\xcf\x66\xb7\x46\x1b\xfc\x3c\xf5\xee\x25\x51\x7e\x90\x26\x09\x77\xd9\x84\x96\x5b\xd7\xa0\xbd\x8e\x77\xbb\x7c\xf0\xc0\xbb\x8d\xe2\x2a\x3b\x5d\xff\x0e\xc6\x9d\x14\x48\x99\xbc\x94\xe6\xc1\x38\x14\x02\x27\x08\x89\xc6\xab\xb7\x65\x75\x4b\x67\xa2\xf8\x84\xc0\x55\x4e\xc6\xc1\xa2\x35\xdc\xde\x64\x49\xf8\x48\xd2\x1a\x6e\x6f\xb1\x34\x7d\x1c\x68\x0d\xb7\x1f\xab\x14\x2e\x3a\xb5\x86\xdb\x4f\x55\x12\x16\xee\x5b\xc3\x9d\x6d\x95\xc1\x56\x78\x6b\xb8\xb3\xa3\x13\xb4\x50\xdf\x1a\xee\xe8\x4a\xf5\xb1\xb0\x35\xdc\xf9\xd6\x49\xa6\xc5\xac\x35\xdc\x79\xea\xa4\x27\xb4\x68\x0d\x77\xbe\x73\xd2\xa5\x20\xdc\x1a\x3e\xde\x74\x32\xf3\xd9\xac\x35\x7c\xbc\xe5\xa6\x33\x59\xb8\x35\x7c\xac\xbb\x2f\xcf\x38\xad\xe1\xe3\x6f\x54\xa2\x79\x70\x6e\x0d\x1f\x3f\x51\x59\x52\x6a\x69\x0d\x1f\x7f\x5b\xad\xdb\xbb\x3e\xed\x6d\xef\xdc\x6b\xde\xee\x35\x6f\xff\x2d\x9a\xb7\x20\x8e\xc1\xbf\xc4\xed\xdc\xb8\x22\x05\x97\xa3\x0a\xf1\xe9\x42\x64\x94\x98\x97\x67\xdc\xa2\x1f\xe9\x18\xa0\x37\x12\x4e\xc7\x8c\xa9\x0b\x8e\xe4\xea\x69\xbc\x8a\x9a\x1f\xe1\x72\xd7\xaa\x0c\xd2\x24\xc4\x39\x0f\x7d\x64\x82\x48\x56\x24\x32\x95\x73\xd7\xfd\x38\x36\x86\x62\x0a\x46\xe6\xd1\xaa\x07\x37\xf5\x3d\x62\x99\x96\x95\x28\x3d\xcc\x04\x7c\x44\xfe\x85\x5f\xce\xb3\xff\x70\xb2\x63\x2e\xc9\x37\x21\xa7\x87\xd5\x51\xbe\x2d\xa9\x55\xba\x03\xdf\x53\xbf\xae\xae\x20\xfc\x0d\xb1\xdd\x3e\xb0\x44\x48\x3d\x69\x33\x29\x14\xc2\x0a\xb4\x7b\xa4\x5d\xa4\xfc\xe7\x69\x9f\xa3\x19\x85\x3b\x9c\x78\x6e\x43\x45\x33\x27\x93\x53\x30\x70\x51\xf6\xa1\xe2\x86\xb4\xeb\x89\x99\x6d\x55\xc3\xfa\xc3\x8a\xef\x21\xe2\xe1\x1e\x74\xa0\x23\xfc\xbc\xa4\x63\xe0\xe9\x06\xa5\xcd\x82\x7e\xb7\x05\xae\x28\x34\x5e\x0d\x3c\x9b\x8f\xbb\xb0\x73\x8a\x2a\x8c\x7b\x82\x16\x87\x41\x11\xc8\x11\xb0\xdf\x7d\xf6\x0f\xd9\x43\xbf\xaf\xae\xc0\x28\x56\x01\xc0\x55\x72\x2e\x41\xc4\xd7\xd5\x95\x0e\xbe\x09\xda\x46\xd6\xb4\xbc\x23\x47\x80\x27\x9b\xa7\xfd\x9c\x31\x04\xe5\x61\x9d\x41\xcf\x85\x80\xa3\x29\xcc\x9d\xae\x5f\x3c\xd3\x85\x5b\xd9\x13\xa6\xb6\x42\xba\x73\x2f\x6d\x3b\xbf\xa8\xe7\xe9\xdd\x93\xcd\x53\xf4\xf0\x6a\x1d\xda\xef\x92\x2f\xf0\xd4\x21\x48\x92\xb4\x20\x93\x28\x09\x79\xbf\xa2\x64\xca\x1b\x7a\xa6\x9a\x1f\xa7\x49\x9e\xc6\xb4\x7f\x1e\x64\x49\xa7\x8d\x4b\x70\x67\x39\x8c\x15\xc7\xe9\xb4\x8d\x4c\x5f\x45\x8f\x19\x2a\x1c\x87\x4b\x54\xb0\x21\x1c\x98\x0b\xe6\xae\xe3\x5b\x9d\x3d\xde\xad\x9e\x49\x10\xe6\x11\x0a\x6a\x94\xbe\x0e\x61\x8a\x1b\x2c\xc7\x0b\x3a\x66\x12\x80\x67\x3d\xf6\xc0\x21\xd3\x28\x18\x7f\x56\x21\x44\xc1\x13\x81\x38\xec\xca\xeb\xd6\x4e\x90\x4d\x97\xf0\x12\xe4\x44\xfd\x42\xce\x78\x4c\x2b\x74\x59\x23\x84\x7e\xae\x2c\x86\xdd\xc6\x75\x1c\x08\x36\xf1\x5b\xa6\x1b\x0b\xcd\x36\x92\x65\x1c\x3b\xe8\x4e\x25\xa5\x09\xe7\x77\xfa\x00\x2c\x21\x26\x28\xc8\x1a\xd7\xcc\x02\x26\xfb\xa3\xc8\x54\x1a\x22\xf1\x9b\x73\xf6\x4e\xda\x83\x83\x52\xbb\xe7\x65\xac\x3d\xc9\xde\xd9\x61\xab\xd3\xed\xe9\x86\x10\x86\xeb\x67\x2a\x28\x8a\x60\x3c\xfb\x98\x1e\x48\x3f\x58\x78\xca\xa4\x73\x2c\x7c\xe6\xd6\x53\xcb\xc7\xcd\x3f\x9d\xe1\xc8\xa2\xfd\x20\x8e\xd5\x7e\x22\x80\x4b\xce\x14\x4e\x37\xd5\x01\xc3\x73\xc2\xf0\x1e\x31\x80\x54\x5b\xc3\x6d\x90\xee\xf9\xaa\x6f\x0d\xb7\x41\x76\xc7\x21\xdb\x76\x00\xd8\xda\x08\x5b\xc3\xc7\x3b\x4c\x64\x7e\x7c\x2f\x32\xdf\x8b\xcc\x7f\x6d\x91\x19\x45\x7b\x81\xb3\xf7\x5d\x85\x7b\xf9\x7b\x9e\x26\xd9\x62\x6c\xca\x9b\xbf\xf0\x44\x75\x75\x98\x65\xa9\x2d\x02\xf3\x34\x25\x89\xba\x2a\x0a\x36\x58\x43\xc8\x74\x64\x4c\x40\xc7\xa7\x52\x49\x53\x64\xe4\x22\xae\x77\x8d\x9f\xc0\x20\x0c\xa5\x4b\x47\xc6\x8e\x45\x61\xf0\x92\x0d\x5d\x13\x09\x96\x45\x60\x10\x86\x1e\x1b\x5b\x22\xc6\xcf\x0b\x15\xda\xba\x75\xb0\x06\xe3\xc4\xac\x38\x0c\x7d\x32\xb7\x6f\xe0\x39\x0f\x0a\x2e\x21\x6a\x47\x24\x99\x76\x55\xff\x05\x8c\xb7\x6b\xbe\xfd\xdc\x74\x2e\xa0\xf0\x6b\x74\xd3\x9d\x02\x7d\x4f\x94\x84\x5c\xcd\x24\x61\x7b\xa8\x6e\x9a\x65\x3d\x21\x89\xe6\xae\x4c\xcc\xc9\x87\xff\x12\xc2\xa2\x06\x10\xf8\xc1\x1e\x26\x15\x2a\x7b\x04\x5e\xb7\x97\x3c\x60\x13\x55\x9e\x00\xcc\x29\x3e\x1e\x94\x0a\xec\xbc\x48\x49\xb5\x4c\xac\x91\xfd\x11\x95\xf6\x1d\xd9\xc7\x2e\xb0\x2e\x16\x51\x3f\xca\xff\x11\xc4\x51\xf8\x9e\xe6\x8b\x34\xc9\xa9\x68\xca\x79\x7c\xe7\x8c\xc1\xdf\x5e\x87\xaf\xb1\xfe\x61\x72\xe6\xad\x75\xd7\xa9\xf4\xda\xed\x5f\x69\xe5\xdc\x65\x93\x33\x58\xbe\xe7\x82\x6b\x08\x5f\x86\x68\xbc\x2f\xfa\x00\x4e\x23\x70\x82\x13\xc4\x5e\x4f\x85\x3a\xdf\x10\xbf\x28\x01\x94\xa5\xf5\x93\x7c\xf0\xad\xe1\x36\xe8\xd1\xc4\x8a\x6c\x0d\x77\xc0\xea\xad\x51\x90\xef\xfb\x0d\xff\x7e\xc3\xff\xf3\x6e\xf8\x7a\xbf\x57\x62\xf9\x1d\xa9\xc8\x1a\xea\xaa\xd8\x89\x27\xb3\xc0\x72\x21\xeb\x0f\x20\x73\x55\x75\x9a\x84\x43\xef\xa6\xb0\x1e\x4c\x3e\x88\x12\xd0\xfb\xe8\x10\x82\xc0\x94\xc6\xd0\x88\x38\xee\xdb\x3f\xb9\x7a\x09\x3f\x32\x83\x6d\xde\x7e\xa7\xcc\xe1\x0e\x34\xd8\x3b\x09\xa5\xe4\x02\x30\xf6\xbd\x26\xd2\x95\xb3\x99\xea\x6d\x40\x38\xfb\xf5\x57\x6d\x3e\xf5\x1c\x45\x3d\x51\xce\xba\xd5\x09\x46\x91\x47\x0d\x82\xdc\x3e\x13\xcb\xcf\x32\x8f\xef\xbd\xb7\x47\xda\xa8\x4f\x6d\xf2\xe0\x81\xe1\xc7\x19\x9d\x9b\x79\xb3\x86\xb3\xff\xeb\xae\xb5\x0d\x57\x35\xe8\xf1\x0c\x4d\x3a\x90\x58\xb2\x5d\x43\x1e\x77\x18\xed\xd9\x19\xac\x8a\x18\x58\xee\x69\x1a\x68\x4f\x1c\xde\x39\x42\x39\xa8\x42\x23\xd2\xf2\x48\xed\x55\x03\xe9\x51\xc5\xf3\x12\x9e\xa2\xf8\xd1\xda\xfb\xb2\x29\x08\x43\x49\xc3\xb9\x3e\x86\x63\xda\x90\x69\xd7\xaa\xa6\x52\x7a\xe2\xa4\xe2\xaf\xb2\xf2\x64\xaf\x8f\xeb\x37\x27\x14\xf4\x0a\x71\x95\xd9\xc7\x9a\x2a\xa5\xfd\x51\xfd\xf9\x44\x8b\x99\x54\x37\xeb\x4e\x9a\x7e\x2f\x6a\x55\xa9\x13\x47\xcd\xa1\x11\xa0\x55\xa5\x0d\xe6\x95\x73\x8b\x46\x93\xca\xf9\xcd\xdd\xcd\xa8\x5d\x5f\xbd\xa2\x46\x32\xbc\xbb\x98\x5b\xce\x7b\x2d\xb5\xb2\xe0\xac\x42\xdb\xa8\x78\xac\x39\x79\xae\xde\x8a\x77\xac\x74\x3a\xf7\xe3\xb8\x72\xba\x00\x48\x5c\xf4\xac\x4c\x60\x5c\x15\x5a\xd3\xc1\xd5\xa9\xcd\x78\x14\xe8\x2a\xd5\xca\xa8\xad\x8a\xdc\x94\x9b\x1c\xb0\xfd\x93\x93\x3e\xa5\x45\x2e\x8c\x57\xe2\x4b\x12\xd2\x45\x9c\x5e\xd2\x50\x9a\x08\xc2\xf3\xc1\xf1\x2c\x88\x12\xfb\xb9\x1a\xd4\xf6\x63\x9a\xc9\x1e\x79\x7c\x0f\xc8\x03\xab\x8f\x24\xe5\xba\xbc\x56\xaa\xc5\x35\xc3\x43\xee\xb1\xbc\xdc\xd0\xcf\xda\x4a\x5a\xc4\x06\x0f\xb2\x25\xa4\xb0\xd4\xe4\x0b\xf1\x9a\x21\x90\x8c\xa3\xe6\xfd\x11\x82\x94\xef\xc9\x87\x65\x90\x3f\x18\x90\xf3\x20\xe2\xea\x72\x10\xb9\x16\x85\x56\xc1\xca\x9b\x32\x73\xde\xc5\x52\x50\xf1\xa2\x75\xc7\x68\xd7\x74\xbc\xbc\x4e\xe1\x69\xb2\xd1\xbe\xbd\x2b\x41\x7f\x37\x36\x76\xcd\x63\xd3\x60\x40\xf2\x22\x5d\x70\x5d\x6d\x94\x4c\x49\x30\x61\x5d\xf9\x66\x93\xcf\x55\x4e\x3a\x45\x34\xa7\xe9\xb2\xe8\x3a\x47\x47\x8e\x80\x1f\xc8\x37\x9b\xde\xc3\x22\xef\x7d\x9f\xd5\xfe\xb3\xa8\x5c\x87\x54\xe8\x92\x2f\xd7\x9e\x33\x9d\x8d\x40\xfe\x60\xcf\x7b\x0e\x55\x33\xe2\x3d\x6d\xea\x93\x9f\xf6\x0b\xac\x18\x13\xdc\x97\x04\x7c\x65\x8c\x19\x61\x83\x8f\xe0\x11\x93\x98\x97\x49\x68\x63\xa0\xed\x3b\x7c\xd2\x18\x39\x14\xc1\x7f\x8e\x37\xe2\x1b\xb7\xca\x96\x1f\xae\x59\xf9\x13\x71\xb1\x66\x50\xcd\x94\x16\x1f\x75\x53\xef\x39\xa9\x69\x8e\x82\xba\xf1\x2a\xc8\x67\x98\xa8\x7a\x92\x30\xbb\xfe\x23\x7c\x34\xe9\x08\x00\x3f\xb5\x79\x0b\x79\x3b\x08\x11\x8c\x44\x5d\xfd\xb1\xb9\x00\xcd\x1e\x41\x98\x23\x7f\x77\xe4\x5f\x99\xf3\xf6\x27\xca\x79\x7b\xd9\x5f\x34\xe9\x98\x14\x77\x75\x45\xd6\xa1\xc5\xca\x62\x44\xb1\x6e\x0f\x6d\xe2\xbf\x9b\x2c\x01\xfc\xd7\x70\x39\xd8\x43\x4a\x43\x14\x21\x7a\xa7\x72\x66\xe4\xdf\x60\xa0\xee\xf9\xe2\x74\x8a\xa8\x16\x8e\x15\x92\x8d\xaf\xb7\xbb\x35\xcd\x13\x43\x54\x53\x1c\xb5\x64\xaa\x1b\x54\x36\x18\x10\xbe\x59\x49\x71\x21\x48\x42\x22\x6e\x46\x48\x30\x0d\xa2\x44\xac\x9c\x73\x2a\x02\xfc\xd5\xfc\xf9\x65\x4f\x7b\x03\xac\xa9\xc1\x96\x75\x9c\xed\xbf\x66\x48\x63\xee\x96\x4d\x5c\x0a\xb2\x2d\x81\xed\x8e\x39\x1d\xa7\x49\x48\x18\xc3\xad\xad\x04\x91\x6e\x3d\xb1\x12\x83\x23\x82\x2e\xac\x69\x87\xbd\x5e\x8c\xee\xb8\x43\xd8\x75\x3b\x12\x25\xc4\x89\x16\x71\xca\xbc\x48\x33\x1a\x2a\x37\xee\x5c\x02\x01\x8d\xcf\x34\xc8\x49\x30\x67\x1b\x52\xdf\xcb\xaf\xed\xbf\x52\xfe\x6d\xff\x79\xbc\xcb\xdf\x45\x17\xab\x7b\x78\x5d\x9a\x5b\xc6\x31\xdc\x12\x36\x24\xd2\x4e\x36\x3d\x50\xa0\x2b\x06\x49\xe8\x3f\x06\xec\x98\x7d\xa9\x7c\x69\x58\x52\x9c\x05\x56\x73\x68\xb0\x2b\xc5\x07\x06\x38\x55\x05\xa3\xc8\xb8\x5c\xe0\x2f\x8a\xa8\x3c\xbe\x43\x5a\x30\x8a\xc8\x1e\x83\x94\x72\xd6\x43\xae\x09\xad\x1f\x93\x3e\x21\x25\x24\x40\xa2\xa9\x28\x2e\x6b\x91\x63\x4b\xe8\xb9\x4a\x92\x63\x4a\x2e\xaf\x31\x31\x58\xba\x91\x2d\x69\x53\x10\xc4\xdd\x15\x8b\x6e\x57\x14\xb5\xe5\x60\x43\xb2\x10\xbe\x4e\xa4\xa2\x38\x74\x4a\xfb\x24\x65\x01\xa1\xa4\x65\x7d\xfc\x93\x49\xaa\x2d\x3d\xf1\x50\x68\xa0\x27\x82\xa1\xd4\x77\xfd\x42\x2a\xb6\xe8\x6f\x65\x0d\xec\x4f\xfd\xe0\xd2\xb5\x3a\x45\x62\xfa\xeb\x48\x3a\xe8\xa9\xd9\xc7\x1c\x6c\x30\xe0\xa1\x15\xb5\x95\x85\x51\xa9\xb6\x95\xf8\x72\xbd\xcb\x80\x25\x96\xd6\xcd\xb6\x05\x62\x50\xc5\x70\xc6\xcd\xe0\x2d\x0e\x10\x32\x7e\x94\x10\x47\x63\x0a\x57\x0d\xda\x5e\xc3\x8a\xfe\xe7\xb3\x1d\x01\xfb\x8f\x72\x8b\x11\xe2\x58\x8d\xe4\xfd\x45\xba\x30\x1c\xcc\x99\xdd\x8b\x83\xbc\x10\x90\x4e\xd5\xfe\xee\x70\x42\xea\xb0\x82\xe0\xbc\x68\x5d\xbd\x38\x81\x38\xb4\x90\x6e\xf7\x49\xa3\xb0\xa6\x4b\xac\x21\x01\xdc\xe7\x41\x49\x7e\x20\x9b\x76\x6d\x62\xa6\x25\xed\xef\xcb\xb5\x5c\xaf\x05\x90\x7f\xb7\x52\x09\x22\x34\x59\xcc\x52\xaa\xd3\x94\xa9\x1d\x1e\xd6\xba\xd9\xe5\xfe\x22\xb8\x0c\x46\x31\xf5\x75\xcf\x3d\x0e\x70\xfb\xa9\x9c\x26\xa1\x0e\x48\x95\xa4\xc9\x23\x51\x09\x46\x87\xbd\x4d\x5c\x97\x4d\x3d\xf8\xf6\x63\x9c\xd1\xaf\x82\xed\xc8\xa5\xd2\x83\x11\xa3\x5a\xe5\x04\x81\xed\xdb\xc6\x1e\xaf\x68\xd7\x9c\xc4\xd2\x1b\x41\x7c\xa2\x35\x74\x00\x52\xee\x83\x58\x08\xa6\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\x07\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\x02\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\xcb\x63\xa4\xbe\xb4\x2a\xef\xf0\x7d\x79\x30\x30\x7c\xe9\x96\x86\x9d\x1e\x8f\xc1\x2d\x6e\xca\xe3\x34\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\xc4\xaa\x81\x08\x61\xd2\xa8\x40\x75\xa9\x27\x0b\x8c\xe8\x38\x58\xe6\x94\x9d\xc4\xc3\x34\x69\x17\xe4\x3c\x48\xc0\x28\x29\x5f\xa4\x51\xcc\xaf\xc3\x93\x82\x66\x93\x60\xac\xbc\x63\x37\x38\x8a\x37\x39\x6e\xdb\xfb\x54\x3d\x43\x24\x8e\x7f\x5d\xb5\xa8\xd1\xe2\xfc\x89\x16\xdc\x5d\x33\xdb\x20\x7b\xe4\x7c\x16\x8d\x67\x60\x35\xc0\xd6\x77\x91\x8a\x7d\x8c\x2c\xe2\x65\x5e\x7f\xf7\x2a\x18\x41\xcd\x04\x6b\xee\xe1\xb7\x64\xaa\x91\x61\x57\x17\x54\x55\xb1\x7a\x01\xf2\x36\xc2\x63\xb9\xe0\x88\xac\x95\x6f\x24\xc8\x54\x09\x31\xe6\x53\x87\x3e\xb7\x48\x6f\xce\x7d\x3d\xc7\x1e\xef\x79\xb7\xc1\xfd\x79\x19\x6f\x72\x4e\xc3\xde\x63\x70\xc9\x53\x16\xdf\x81\xd8\xdd\xfe\xb4\x61\x38\xc7\x9f\xfb\x7a\x85\x78\x4e\xd3\x5e\xbb\x25\x8b\x6e\x77\x95\xfd\xb3\x69\x2c\xd1\x1a\x7e\x5b\x66\x02\xad\x4c\x1a\x5a\xc3\xed\x1d\xd7\x26\x5a\x8c\xbc\x35\xdc\xd9\xba\x3e\xed\x6d\x3f\xb9\xb7\x7d\xba\xb7\x7d\xfa\x6b\xdb\x3e\x21\x63\x67\x61\x03\x79\x07\xd6\xce\x25\x7e\x2c\x85\x75\x25\x7f\x98\x75\x34\x91\x97\xce\xfb\xd9\x34\x1f\x96\xa8\x6e\x90\x90\x27\x8e\xb0\xa2\x12\x1c\xfb\x4e\x6e\x27\x8c\x7d\xca\x4a\x09\xb6\x71\x02\x3e\xdf\xf3\xf5\xe1\xfd\xbb\x03\xce\xdc\x6f\xd3\x01\x1e\x70\x09\x58\x2d\x85\x17\x8c\x45\x4a\xde\xbf\x3b\x10\x17\x05\xfe\x0e\x88\xf7\xe8\xe0\x45\x51\xb7\x3c\x4b\x73\x7c\xfd\xe5\x36\x7e\x70\xf4\xf6\xed\xcb\x83\x8f\x87\x47\x6f\xc9\xcb\xf7\xef\x8f\xde\x0f\xc9\x81\xd2\xff\x8e\x79\x95\xfc\x48\x1f\x52\xd2\xde\x20\xac\x3e\xb2\xd1\xee\xfb\xfb\xa0\x5d\xde\x34\x1d\xbb\x7a\x68\xcf\xb5\x08\x05\x5b\x3d\x11\xaf\xcc\xdf\x84\xb4\xa4\x1d\x12\xdb\x2a\x18\x0d\x13\xde\xa5\xd1\x3c\x0f\xa6\x94\xec\x91\xf5\x75\xf1\xd4\x90\x6d\xeb\xe2\x77\x9f\x87\x8c\x75\x52\xfa\xb2\xd8\x33\xe2\x4d\x1e\x12\x35\x5d\x7f\xff\x70\xf4\x16\x66\x25\x53\x5d\xf2\x84\x59\x15\x7d\x73\x1e\x93\x69\x1c\x88\xaa\xcd\xd1\xea\xd9\xfc\xc8\xef\xab\xf1\x78\xe7\x79\xd3\x29\xfd\x78\xf8\xe6\xe5\xd1\xf1\xc7\x21\x11\xb7\xde\x8c\xb8\x58\x27\xe7\x39\xd9\x20\x6d\xf6\x5f\x30\x9e\x31\x8e\xd1\x36\x62\xda\x08\x3f\x92\xdf\xde\xef\x56\xf7\xbb\xd5\x5f\x7b\xb7\x42\x9b\x15\x3c\xbb\xfc\xa3\x9a\xe9\x36\x7f\xcd\xde\xe8\x11\xfd\x1d\xbe\x65\x97\x4e\x87\xd8\xfa\x57\x87\x33\x1c\x93\x29\x37\x8e\x21\xe2\x91\x2d\xb4\xa5\x0f\x0b\xb6\x15\xf2\xd7\x7e\x08\xbf\x90\xb6\xbc\x48\x93\x8e\xf3\x79\xec\x0a\x52\xf1\x1e\x39\x4f\x93\x6e\xcd\x1b\x7a\x94\x99\xa4\xc9\xe5\x3c\x5d\xaa\x16\x55\x42\xc9\xe9\x4d\x22\x6d\x4a\x25\xae\x68\xc8\xe5\x01\x88\x62\xe0\x84\x6b\x12\x69\xea\x78\xf6\x3c\x4d\xe3\x6b\x08\xaf\x1a\x82\x0f\x72\xbe\x49\x50\x0e\x19\xa2\xd9\x81\x07\x22\x34\x34\x3c\xa6\xcb\x13\x1f\x44\x23\x60\x8b\x52\xd4\x3e\x58\x33\xa6\x09\xbb\xdf\x62\x10\xa6\xe7\x28\x5e\xaf\x1d\x81\x01\x21\xdf\xbd\x13\x89\x3c\xa2\x42\xd4\x17\x35\xc1\x05\x87\xf8\x5d\x62\xef\xea\x2f\xaf\x0d\x96\x4b\xaf\x88\x31\xb6\x39\x7d\x86\xdc\x07\x38\xb8\x31\xb2\x70\x1d\x6a\xf7\xe0\xde\x70\x41\xde\x0a\xca\x51\x87\xaa\xab\xf2\x12\xc4\x29\xd1\xf5\x50\xde\xd1\xf4\xda\x7c\x74\xb0\x42\x3d\x43\x2b\x84\x43\xf3\x8a\x71\xe1\xa2\xd5\xf4\xb0\xd2\x88\xa4\x2b\xf5\x1b\x0d\x27\x8f\xa6\x49\x50\x2c\x33\x7b\x38\x38\xbd\x6c\x3c\x18\xa6\x7c\x3c\x0a\xaa\x6a\x40\xe0\xc1\xa0\x79\xff\xc5\x13\x07\x49\xde\x82\x23\x05\x49\xa8\x54\x4b\x45\x0a\x41\x89\x27\x51\x12\xc4\x7e\xb3\x67\x5e\x87\xcf\xa8\x14\xaf\x6b\x2b\x4b\x54\x6f\x20\x45\xe6\xd1\x33\x9a\x5d\x16\x33\xae\xb2\x9e\x8f\x22\x60\x19\x29\x8f\x12\x0d\x7d\x13\x71\x16\x2a\xb1\xe5\xf1\x0d\x22\xba\xe3\xb8\xb6\x53\x8b\x5b\xfd\x42\xaf\x00\xef\x3d\x88\x68\x7f\x1d\xca\x41\x47\x9d\x6b\x11\xa9\xd7\x5c\xb7\x76\x1e\xb7\x9f\xa2\x72\xfe\xb2\x55\x38\x17\xe4\x8e\x3a\x25\xb5\x77\xba\xae\x4a\x53\xcc\xd3\x47\xd9\xb1\xdb\xb2\x74\x14\xc3\xa2\x92\x9f\x83\xe7\x65\x11\x4c\x5b\x94\x3f\x89\x20\xc6\x94\x65\x0d\x20\x80\xf0\xfc\x31\xba\xd1\xc9\xc9\x32\x8e\x4b\x9e\xb8\x68\xcd\x22\x71\x6f\xff\x4d\x85\x30\xd4\x57\x16\x9a\x11\x32\xad\xd1\x9c\x55\x5c\xf7\x0b\xec\x3b\x8f\x63\x3a\x7c\xfb\xea\x91\x33\xfb\xea\xbc\x6b\x07\xd7\x5b\xa9\x36\xe8\x7b\x0d\xc5\x99\x44\x32\x4e\x93\x71\x50\x74\x8c\xd9\xef\x96\x3b\xb2\x29\xe5\x7a\xc2\x8b\x4d\x39\xd7\xb3\x77\x5b\x5a\xc6\xe1\x42\x7e\xf7\xe0\xf2\x30\xc1\x15\x84\xe5\x10\x9c\x10\x78\x2d\xa1\x6a\xf6\xc1\x03\xd0\x37\x98\xbd\xa8\xde\xa6\xcb\xbd\xef\x00\x0e\xee\xd0\xfd\x4e\x90\x4d\xad\xd5\xa5\xc5\xc7\x67\x46\xc9\x21\xfe\x12\xae\x79\xb6\x90\x2b\x14\x31\x3e\x71\xff\xa2\xea\xb5\x9f\x6a\xf1\xc9\x24\x5f\x94\x94\x86\xeb\xdb\xee\xee\xb2\x95\xf9\x4b\x1a\x25\x9d\x56\xcb\xad\x5c\xbd\x8a\xe3\xe4\xc6\xf1\x84\xaf\x37\x40\x36\xec\xb0\x65\xde\xed\xe1\x1e\xe1\xab\x9a\x24\x2d\x0e\x8d\xbe\x2a\x14\x7a\x1c\x0e\x69\xe0\x86\x6d\xc3\xb3\x85\x6e\xcf\x6a\x05\xb7\xaf\x36\x12\xc4\xb5\xd3\x65\xb1\x58\x16\xaf\xd3\xa9\x66\xd7\xc2\x19\x0f\x5a\x2d\xd2\xfb\x0f\x77\x34\x83\xc4\x32\x13\x4c\x73\x6b\x18\x93\xed\x07\x8a\xc3\xf0\x5b\x2e\x83\x9f\x66\x34\x5c\x8e\x29\x9a\xab\x60\x3c\xee\x11\xe1\x8b\x12\xf3\x93\x60\x3c\x3e\x11\xc9\x9c\x27\x32\xa4\x88\x6f\x49\xe5\xcf\xcc\x29\xeb\xe7\xb3\x68\x52\x74\xba\x64\xe8\x60\x54\x66\x39\x4a\xab\x60\x3c\x96\x5a\x2a\x6e\xed\xcd\x49\x9b\xc6\xb4\xa0\x72\x1c\xda\x4b\x92\x99\xce\xa9\xea\x06\x2c\x03\xdd\x5f\x89\x87\x25\x62\x69\xb3\xad\x9e\x8b\x71\xa5\x9e\x15\xee\x4a\x2e\x32\x1a\xae\x16\x7e\x3c\x9e\x1b\x6c\xe9\xe7\x8f\xee\x92\x69\xbb\xde\x25\x53\x55\xf1\xad\x72\x23\x3b\xb3\x02\x62\x48\x80\x86\xf3\x07\x5b\xec\xb0\xfd\x3e\x39\x02\xe5\x1f\xca\x01\x54\x29\x2d\x63\xdb\xff\x06\xaf\x1a\xad\x67\x6d\xde\x27\x8d\x95\xd4\xf8\xb5\xbc\x4d\x31\x50\xf3\xe4\x5a\xc6\x01\xa5\x81\x21\xb4\x74\x82\x00\x4e\x0d\xea\xf5\x01\x60\x07\x56\x9a\x28\xbc\xa0\x27\x8a\xdd\xf3\xb6\x4f\x4b\x07\x60\x58\x4d\x78\xef\x84\x0d\x5c\x22\x97\x58\x55\x57\xc2\x75\x8e\xb2\x6e\xe8\x1b\xeb\x69\x13\x05\xfc\x6d\x9d\x5d\x0e\xfc\xba\xc9\x37\x9c\x06\x3d\xfa\xbf\xea\x48\x22\x38\x88\xc8\xda\x60\x40\x3e\x1e\xbd\x38\x1a\x92\x8c\x72\x8b\xac\x1e\xc9\x53\x61\x3a\xa3\xae\xb8\xb4\x31\x4e\xc0\x35\x5d\x7d\x56\x2e\x2a\xda\x39\x49\xe8\x98\xe6\x79\x90\x5d\xb2\xc5\x02\x21\xb0\x73\x46\x6e\x6d\x70\x58\x0c\xee\xa2\xc9\x79\x9a\x7d\xe6\x52\xde\x7c\x19\x17\xd1\x22\x46\xa1\x1c\xcc\xe0\x29\x7e\xff\x46\x83\x87\xc4\x6b\xcc\xfd\x8d\xb4\xe5\xe6\x75\x98\x66\x0c\xb2\x79\xc3\x88\x54\x37\x46\x43\xbe\x71\x98\x27\x13\x55\xaa\x2f\x71\xe4\xf3\x60\xb3\xce\x3a\x77\xe2\xc2\x9e\xfa\xce\x0f\x65\xb0\x16\x3b\x25\x8e\x81\xa3\xd9\x4f\xe1\xd0\xc9\x57\x53\x8d\x1d\xa4\xb7\x9e\xd2\x23\x94\xae\x5f\x10\xbc\x3d\x26\x07\xc0\x73\xe4\xe6\x39\x3e\x6c\xf0\x1c\xc5\xf4\x84\x49\x8f\xd9\x45\x8f\xe9\xa7\x28\x96\xd3\xc2\x0a\x15\xe3\x73\x72\x55\x79\x10\xab\x9e\xee\x88\x56\x8c\x57\xc3\x78\x86\x5c\x46\x2f\x44\x47\x39\xb9\x5c\x79\xd8\xaa\xe0\x1d\x0c\x9c\x20\xc3\x51\x7a\xd1\x37\xd8\x91\xfe\xd8\x25\x12\x40\x72\x21\xf8\x7f\x57\xa6\x2a\x96\xc3\x7f\xa8\x74\xc4\x68\xe4\x4f\x53\x8e\xa4\x17\xe2\x7d\xb7\xcb\xcd\x39\x1a\xb4\x6b\xa2\x12\xfe\x5c\xc2\x91\x5b\xc3\x1d\x70\x61\x84\xbd\x86\x33\xc6\xfc\xdd\xfd\xcd\xe8\xfd\xcd\xe8\x5f\xfb\x66\x54\x5c\x8b\x8a\x37\xbf\xff\x15\x01\xf6\xee\xd4\x65\x38\x1c\x02\x1e\x92\x83\x34\x39\xa3\x8c\x15\x05\x22\xe6\x31\x9c\x83\xe1\x2c\x00\x81\x8b\x65\x24\x17\x46\xc0\x41\x9c\xa7\x24\x88\xe3\xf4\x3c\xe7\xf1\xd9\x41\x51\x97\xf7\xd7\x58\x45\x52\xf0\x7f\x13\x5d\xd0\xf0\x9a\x67\xad\xb9\xf7\x1a\x6b\xe2\x46\xb5\x48\xed\x28\xc7\x42\x65\xa9\x0e\x9c\x1d\x53\x25\x4a\xae\xae\x64\x84\x74\x9d\xd1\x56\x3a\xd4\x76\xd7\x56\x06\xf0\xb3\x9c\x10\x91\xb8\x62\x96\xf7\xa1\x23\xf5\x8b\x46\x43\x5c\x0f\x71\x34\x01\x55\x73\x17\x6a\xdf\x74\xea\x04\x48\xc1\xf7\xf1\x93\x56\xe3\xce\x48\x46\x51\x52\xed\xc0\x91\x8b\x89\x9a\x8c\xd3\xca\xcb\x1f\xdb\x12\x36\x55\xfa\x7d\x71\xd8\xea\xb1\x49\x38\xa3\x59\x34\x01\xc7\x1e\x19\x1d\x07\x8c\xe3\xa0\x48\x35\x0f\x1e\x90\x38\xf8\xf5\x92\xc4\x69\x10\x92\xf0\x32\x09\xe6\xd1\x98\xa4\x09\xcd\xa1\x35\x31\x21\xba\x21\x11\xcd\x3a\x55\x7a\x02\x80\x92\x06\xf6\xb2\x71\x07\x8a\xcd\xd6\x94\x16\x47\xea\x90\xec\x71\xe1\xcc\x26\x46\x0b\xac\x75\xfe\x01\xb0\x32\x41\x4c\x89\x3c\x26\x97\xdf\x7a\x18\x9a\xfe\xd2\xab\x17\x9e\x9d\x9f\x47\x10\xb0\x04\xf5\x8a\x80\x0e\x22\xa7\xfc\x04\x3d\x74\x9e\x56\x71\xe1\x7d\x99\x51\xa1\x5e\xec\xc1\x05\xde\x98\xaf\x0e\x7e\x38\x9e\xd1\x0b\x9f\xda\x40\x6b\x4d\xad\x04\xcb\x15\x65\x83\x22\x86\xe6\x53\xc4\xd5\x2e\x55\xca\x5b\x0a\x7f\x19\x85\xfb\x99\x88\x4f\xce\xaa\x12\x8b\xac\x4b\x86\x72\xbd\x09\x30\x57\x56\xf2\x5d\x13\x78\xde\xd7\x41\x37\x87\x56\xb7\x7b\x0e\x1c\x5b\x02\x1a\x8a\x7d\xb9\x30\x45\x8a\xeb\x71\xf3\x03\x19\x96\x59\x02\x05\x38\x28\xb3\xdd\x1a\xdc\x5f\x0d\x57\xba\xd6\xea\xab\x72\x5d\x5f\xef\x6e\x52\xa3\x28\x65\xea\xa7\xd0\x41\x87\x53\x60\x3e\x63\x14\xe8\x41\xb8\x45\xea\x52\x55\xb3\x1f\x86\xfc\x59\x84\x52\xa2\x05\x49\x48\x72\x5a\xe4\x64\xb9\x80\x0c\x71\x1a\x01\x96\x11\x15\x34\x63\x7b\x47\x7a\x26\x84\x2d\xe1\xc7\xb4\xbf\xb6\x86\x9e\x46\xbc\x4e\xa7\xf9\x7e\xf1\xa1\x08\xb2\x62\xcd\xd6\x34\xe6\x34\x9e\xa8\xc4\x89\xfb\x80\x59\xb0\x70\xb3\x16\x23\x50\x18\x8d\x27\x8e\x13\x1f\xf9\xca\x6e\x4a\x0b\xae\xcf\x62\x85\xad\xa7\x76\xa0\x5f\xd0\xc3\xcc\xa1\x7b\x44\x9e\x3c\x2d\x9e\xc1\x5a\xe9\xfb\x18\x07\x64\x4c\x69\xd1\xb1\x1e\xfd\x08\x4b\x46\xe7\x94\x33\x18\x88\x17\x34\xf0\x4c\x94\xf5\x51\xa0\x0d\xcc\x26\xe1\xa2\x5b\x26\x4a\xb3\x23\x70\x85\xd1\xef\xf7\xc9\x2f\x4b\xee\x09\x98\xb5\xc9\x78\xaf\x73\x5e\x2e\x79\x19\x59\xf1\x2a\xf2\xda\x7e\x02\x6b\xad\x74\x35\x0c\xff\x19\x93\x67\x7a\x0f\xa6\xdc\x90\xb3\xee\x9d\x26\x7f\xbc\x63\x9a\x7d\x1a\xfd\xab\x77\xc4\xfa\xf5\x48\x77\x91\xc6\x31\x27\x1f\x3f\xd9\x0a\xda\xd4\x60\x36\x5d\x2a\x95\x08\xa8\x6d\x93\x37\xca\x0c\xd7\x20\x96\xb4\x84\x5c\xc4\x8c\xa6\xce\x9c\x4a\x23\x0b\x46\x7a\x72\xac\xbe\x49\xf0\x3d\x9b\xf2\xd1\x44\xda\xf8\x24\xdf\x94\x3a\x6e\x46\x19\xda\x4c\x19\x86\xa6\x95\xd7\xcf\xac\x04\x5d\xc9\x50\x16\x72\x49\xe7\x56\xe8\xb9\x1d\x91\x96\xea\x03\xa0\x4f\xb6\x37\x6a\xc6\x78\xde\xa5\x71\xcc\xf8\x8c\xee\x09\xa7\xc1\x21\x2f\xc2\xce\x69\x74\x4e\x93\x02\x8e\x9c\x7d\x46\x71\x30\x34\xbd\x97\x2c\x84\xa1\xfd\x09\xc7\x14\x90\xe3\x61\x78\xda\x93\x57\x54\x46\x72\x4f\x13\xa3\xc8\xc1\x7e\x8c\xb8\x82\x18\xe8\x97\x6d\xd6\x32\x6c\xa1\x43\xe2\x96\x4c\xd6\x23\x4e\x7c\x0f\xb9\xdc\x3c\xb7\x03\x3d\x71\x9a\x3a\xc8\x28\x8c\x09\xec\xb5\x0f\x3c\x2f\x1d\x81\xd9\x71\x0d\x36\xba\x70\x35\xf0\x81\x34\x7c\xab\xa8\xca\x4a\x75\x5d\xa5\xca\x1e\xbf\x52\xcd\xec\x0c\xb2\x25\x20\xa5\x2e\xe3\x4b\xad\x31\xb5\xb0\xa9\xc5\x60\x4b\xf4\x45\xd0\x0e\x1a\xcc\x04\x04\x29\x67\xde\x7d\x32\xa6\x56\x88\xb0\xac\x51\x19\x62\xcb\x3d\x28\xcb\xd7\x6c\xcf\xc9\xc2\xd7\x4e\xea\x77\x69\xbf\xfb\x09\x3d\x17\xb7\x4e\x18\x07\xd8\x59\x18\x67\x92\x51\x68\xf8\xc6\xf3\x33\xc7\x9a\x65\xdf\x19\x8f\x3c\x62\xee\x78\x54\xcb\x07\x89\xe0\xc8\xe2\x5c\x58\x41\xbd\x96\x47\x52\x97\xbd\x54\x94\xf5\x77\xa3\x5a\xef\x6c\x2c\x6d\x46\x04\xa1\xeb\x08\x10\xfb\x6a\xc8\x28\x5c\x32\xb0\x33\xc7\x82\x26\x21\x18\xb8\xa9\x49\x0e\x72\x50\xb4\x24\x39\xa3\x50\xe5\x0c\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\xa3\x9a\x09\xf2\x36\x83\x4d\xc6\xd0\x48\xb4\x3d\x31\x89\xb1\x74\x18\xc4\x19\x0d\xc2\x4b\xfd\x60\x5d\x0b\x8a\xf9\xed\x29\xcd\x14\x64\xa5\xf4\x5a\x37\xae\x68\xd2\xb1\x5a\x53\x4e\xe0\x36\x5d\x97\x5c\x7a\x65\x72\x2e\xee\xf3\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\xd5\x84\xcf\x6d\x15\xd6\x64\x73\x96\xcf\xb6\x1f\x3e\x18\x74\x97\x7b\xee\x84\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\x66\xbb\xcf\x7b\x82\x23\x53\x78\x4d\x4e\x54\x49\x5b\x28\xb7\x76\xd4\x12\x3b\xca\xc1\x80\x1c\x4e\x34\x67\x8c\x72\xf5\xae\xef\x92\x0a\xff\x2b\x24\x2a\x88\x76\xd3\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\x84\x48\xf1\xf0\xb7\xaf\xe8\xa0\xc7\x92\xc7\x33\x6a\x5b\xef\xc5\xe9\xb4\xac\x5d\x62\x31\xa6\x8a\xb3\x05\xd4\x32\x64\x7b\x42\x89\x3f\x3e\x7f\xc4\x12\x13\xc4\x39\x00\xd8\x03\x6b\x4e\x47\x8e\x9f\x29\x21\x88\x1f\xbe\xe0\x09\x43\x41\x63\x9d\x6e\x9f\xef\xc8\xe3\x40\x7a\x2c\x04\xbf\x2a\x34\x24\x6c\x75\xcf\xb2\x34\x49\x97\xb9\x72\x5f\x28\x0c\x03\xd8\x6e\x6f\xbb\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\x82\xbf\x55\x5c\x90\x5e\x9f\xf6\x76\x36\x9b\x5c\x81\xb6\x97\x39\xd7\x8b\x8f\x8b\xf6\xda\xfd\x85\xe8\xfd\x85\xe8\x9f\xf8\x42\x54\x3f\x15\x45\x2a\xeb\x9b\xbc\x17\x15\xc0\x2b\xdc\x64\xfa\x82\xbf\x35\x7e\x62\x9a\x4c\xa2\xa9\x17\x8e\x67\x49\xc0\xc3\x51\x60\x05\x75\x89\x46\x41\xe2\x09\xd4\x02\xda\x64\x1e\x69\x8a\xdb\x48\xf3\xcb\xcc\x51\x34\x15\x1e\x0c\x2c\x2b\x46\x0e\xf4\x3c\x9a\x5a\x4a\x7d\x6c\xcd\xc8\x35\xce\x57\x1c\xe2\x4a\xc1\x5e\x9b\x5e\xab\x74\x3a\xb6\xc4\x05\x3d\x63\x49\x1b\x86\x54\xc4\x7b\xe7\x7d\x86\x56\xa4\xaa\xac\x04\xdb\x55\x4a\xa0\x28\x7f\x97\x51\x71\x0d\x8a\x6e\x27\x8c\xba\x47\x3a\xdd\x6a\x60\x84\x4b\xb0\x83\x84\x70\x7f\x4f\xae\xae\xdc\x3c\x71\x36\xf5\x67\xd2\x20\x8b\x23\x56\x14\x75\x2d\x59\x2c\x8b\x17\x74\x12\x2c\x63\xef\xc5\x49\x5d\x1f\xd9\x8e\x6c\xb7\xa3\xae\x7c\xbd\xe1\x5b\x18\xc9\xf4\x43\xd4\xa2\xc7\xf7\x54\xf9\x3d\x0e\xee\x82\x35\x8a\xdf\xa2\xfb\xf6\x8b\x2e\x2e\xa0\xb0\x5a\x4a\xe6\xd8\x68\xd4\x53\x21\xca\xf6\xe0\x41\xd2\xd6\x2b\x7a\xe1\x19\xb9\x58\x55\x7c\xb0\x39\x32\x8a\x4c\x27\x24\x30\x7c\x03\x82\xe3\x49\x65\x47\xa0\xec\x02\xd8\xba\x7b\xf5\xf2\x9f\xd6\x72\x83\x3a\x98\x5c\xec\x5d\x68\x52\x97\x6f\xf8\xd8\x75\x0c\xdf\xe5\x15\xb9\xd4\xf6\xbb\x75\x7a\x23\x7f\x7f\x31\x2e\x8f\xe1\xfa\x0f\x5d\xc1\xc2\xe7\xd5\x95\x45\x43\xfb\x63\x88\xbb\x80\x1c\x9f\x61\x78\x8f\xc7\x2d\x59\x2d\xf4\x49\xb8\xa1\xf2\x5f\x3d\x9a\x72\x10\xae\xba\x48\x45\xc0\xe8\xa8\x20\xf3\x68\x3a\xe3\x82\xa3\xf2\x5e\x2c\x94\x54\x4e\xcb\x45\x5a\xdb\x6e\x91\x9a\xad\x9e\xb4\xe7\xc1\xc5\x8f\x94\xbe\xa3\xd9\x4f\x41\xde\xee\x11\xf6\xfd\x2e\x8b\xd2\x2c\x2a\x2e\x8d\xf4\x69\x90\xbf\xcb\xa2\x31\x15\xbf\xd9\x7f\x30\xcd\xec\x47\x92\x26\x63\xea\x7b\xc5\xf8\x99\x5e\x56\xbc\x63\xfc\x4c\x2f\x9b\xbe\x64\x84\x9a\x1c\x5c\xf3\x1a\xf6\x90\xdd\xc5\x0b\x3a\x8e\xe6\x41\xdc\xc1\x00\xee\x4b\x32\xf3\xb2\xf5\x6b\x13\x3b\xf2\xb9\x79\xd7\x34\xef\xab\xfa\xee\x49\xff\xa6\xd4\x7d\x4f\xd7\x7f\x44\xba\x16\x42\x91\x43\xd8\x70\xff\x2a\x83\x09\x09\xaa\xf6\x8a\x4a\x8d\xe9\xf9\xc2\x14\x8f\x44\xfa\x9a\x21\x13\xd5\x52\x70\x71\xd1\xfd\xa2\x34\x83\x17\x7d\xbc\x9d\xae\xcb\xd3\xb9\xd6\x88\x99\x00\xca\x43\x46\x2a\xf1\x67\x02\xa8\x37\x20\x2c\x1d\xe1\x02\x5e\x9b\xf9\xab\x77\xa0\xbc\x6d\xd8\x50\x52\xf9\x77\xd1\x07\x92\xf2\x17\x82\x2c\x0d\x39\x0d\x72\x3f\xdc\x34\xc8\x0d\x28\x20\x5f\x04\xaa\x45\x45\x94\x6f\x0c\x15\xaf\x0d\x93\x50\x35\x55\xdb\x60\x25\xf5\x63\x18\xc3\xcf\xa7\x6a\xc9\x59\x75\xd5\x2d\xba\xe0\xe5\x2d\x3b\xb0\x46\x0f\x8a\x8b\xbe\x34\xfc\xf3\x56\x80\x9f\x19\x4b\x2d\xc4\xc5\xca\xcb\x46\x86\xe8\xb9\xc9\xf2\x11\xd1\x82\x2a\x57\x91\x0a\x5e\xb5\xca\x52\xb2\x2b\xb6\x5c\xd6\xe0\xa8\x43\x3a\xca\x50\xcd\xda\xf2\x41\xb9\xf4\xe9\x81\xd2\xa4\x27\x33\x1b\x2c\xb5\x52\xd0\xf2\x26\x4b\x16\x9d\x0a\x86\xb3\x9c\x2f\xe3\xa0\x88\xce\xe8\x4f\x41\x7e\x9c\xc3\x9b\xbe\xb2\xaa\x1c\x58\xab\xae\x69\x6d\x0d\x53\xa3\x1c\x1a\x3b\x9d\x4c\xe8\x58\xd4\xcc\x57\x6e\xc9\x72\x28\x2f\xe0\xa3\xe7\x52\x68\x7b\x51\x9a\xd6\x22\xb2\x58\x9c\x4e\x6d\xe3\x4b\x9d\x81\x22\x0a\x39\xda\x41\x50\xf1\x79\x75\x83\x9e\x07\xca\x0c\xb6\x4e\x11\x28\x5a\x6a\xb4\x0e\x81\xc8\x9a\xaf\x3c\x38\x38\x55\x2e\x36\xa8\xb0\xc1\x52\x33\x6b\xc2\x26\x46\x50\x83\xf6\xd9\x4d\x94\x73\x26\xf0\x52\x28\xf4\x0f\xf8\x68\xd8\x1f\x05\x39\xad\xe5\x8d\x3e\x50\x1f\x19\x78\xe0\x0c\x02\xe0\xf9\xd3\x20\x7f\x1d\xcd\xa3\xc2\x43\xbf\x26\x80\x28\xab\x12\x4b\x88\xde\xc8\x37\xca\xe4\xd1\xaf\xbe\xdd\x4e\x67\x1a\xd0\x45\x34\xa7\x79\x11\xcc\x17\xa5\x45\x14\x84\x5e\x58\x3c\x23\x29\x63\x5b\x46\x76\x59\xb5\x4a\xa7\x82\x3a\x13\x46\x93\x49\x34\x5e\xc6\xf0\xae\xa7\x0c\xd3\x1a\xc8\x1c\x48\x5a\x04\xf1\x8b\x26\x15\x58\x90\x58\x6a\x36\x57\xaa\x00\xd7\x3c\xce\x5c\xaf\x6e\xb6\x2b\x6b\x46\x05\x9d\x77\xed\x17\x7d\x8e\x59\x25\x40\xb9\x17\xd8\xc6\xaa\xf6\x49\x6d\xbc\x60\xdd\xf2\x1e\x71\x9d\x4c\x83\xc5\x1d\xa7\x53\xef\x2a\xc6\x1c\xc5\xb7\x86\xe3\x74\xaa\xd5\x6f\xee\x42\x86\x7a\x8d\xc5\x8c\x2b\xc4\x4b\x19\x5d\x7b\x44\x13\xf6\x65\x6c\x6a\x6a\x9c\x56\x86\x87\xc6\xec\xa2\xbb\xb8\x4e\x67\xd7\x32\x2a\x6e\xb0\xfd\x79\x2b\x31\x9a\x88\xd3\xa9\xa7\x6a\x99\x5a\x52\xa5\x2a\x64\x9e\x2e\xe0\x2a\xa7\xfe\xc4\x7c\x3e\x8b\x72\xc6\x8d\x17\x69\x5e\xdc\xe0\xc8\xfc\x2e\xcd\xab\xa5\x22\x37\x06\x53\x25\xd7\x76\x2b\xc5\x13\xcd\x3a\xa9\xcc\x42\x07\x03\xe8\x74\x7f\x11\x5c\xc2\xbb\x8a\x3d\x43\x4d\x86\xb3\x04\x92\x21\xa9\x28\x62\xef\x79\x4d\x66\x62\xd8\xf3\x34\xfb\xfc\x31\x7d\x97\xa5\x67\xb4\xbc\x0c\x02\xc2\x65\x17\x42\xe2\x2d\x2f\x28\x21\x50\x68\x81\x09\x8e\x3c\x65\x58\x52\x73\xd6\xc2\x3b\xc9\xdd\xac\x60\x9e\x82\xd2\xc9\x9e\xf1\xf5\x8c\x9c\xa0\xcf\x53\x32\x54\x66\x0c\xd7\xba\x55\xae\x83\xe7\xea\xf8\x38\x4e\xcf\xe1\x59\x89\xd4\x6b\x54\x55\x5f\xfd\x0c\x82\xc7\x4e\x64\xc4\x44\xd2\x24\xbe\xe4\x01\x21\x0a\xe3\x75\x86\x7c\x21\xc1\x5f\x42\xf8\x1e\xf6\xc8\x67\x12\x64\x68\x3f\xda\xc1\x0f\x24\xec\xa3\x35\xeb\x63\x23\xde\xa5\xee\x83\x80\xfe\x85\x95\xaa\x97\x9b\xd5\x51\xba\x9f\xac\xcd\x7b\x85\x6a\xc2\x16\x74\x0d\xf8\xa5\x17\x8b\x28\xbb\xf4\xac\x78\x94\x8b\xc9\x2d\xe7\xee\x63\xbc\xd0\x2c\xaf\x6c\x09\x58\xa0\x9e\x05\x00\x94\xed\x93\x25\x2c\x88\xee\xae\x6f\x55\xbe\x0f\xce\x25\xc9\x88\x14\x2f\x18\xaa\x7e\x3f\x1f\x47\x91\xbd\x7c\x65\x19\xbc\xdb\xfe\x3d\x17\x88\x53\x70\x48\x9a\xd3\xeb\x50\x35\x00\xfe\x94\x21\x0a\x9a\x8f\x39\x0c\x06\xab\xac\x08\x58\x9b\x78\x35\x96\x2e\x46\xbd\xdc\x6e\xb1\x92\xac\x6b\x00\x8e\xa2\x66\xf4\xaf\x98\xaa\xad\x91\xf3\x85\x4b\xc1\x66\x3e\x11\xbf\xc6\x4b\xe8\x39\xdc\xe8\x75\xcc\x60\xda\x70\xd5\x31\x0a\x92\x7e\x94\xff\x23\x88\xa3\xb0\x03\xb1\x2e\x44\xca\x8b\x28\xa3\xe3\xa2\xe3\xbb\xe7\x10\x2e\xc5\x00\x50\xd4\xd8\xe9\x3a\x97\x28\x58\xd0\xd1\x31\x88\x64\x0f\x3c\xd5\x1a\x5e\xeb\x3c\x15\x35\xa8\x42\xf4\xcc\xac\x89\xab\x27\x6c\x1b\x12\xe1\x57\x5c\xc2\xb6\x65\x50\x70\xbd\xd0\x3f\x5c\x26\xe3\x28\xf1\x4b\x2b\xc2\x91\x37\xba\x79\x5a\x37\x93\x88\xeb\xd7\xc8\x10\xcf\xc0\x0b\x12\x18\x21\x46\xc9\x14\x0e\x38\xde\xe3\xad\x0b\x66\xfa\x92\x12\x6e\x9d\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\xf3\x2b\x88\x72\xcb\x1b\xc0\x55\xec\xda\x35\x2c\x96\x71\x4c\xc3\xba\x2a\x30\x54\xc9\x51\x57\xbb\x97\x2a\x89\x20\x50\x77\x81\x3a\xac\x85\xe8\xe9\x7a\x2a\x2a\xa8\x29\x69\xdc\xad\x0d\x3d\x69\x08\xd6\x77\x36\x18\x96\x67\xa1\x92\x36\x2f\x19\xfa\x93\x51\x09\x63\xe7\x1b\x7a\xd2\x38\x6c\xd9\xf5\xfb\xb0\x34\x07\x97\xf3\x0f\xa8\x3c\xaf\xa4\xac\xad\x93\xf3\x54\x61\x83\x18\xbd\x37\x54\x0e\x43\x6f\x2a\x86\xc7\xa7\x9a\xa1\x27\x0d\xc3\x5a\x68\xf4\x24\x62\x68\x9b\x0b\x0d\x4b\xd2\x39\xf7\x32\x6c\xb6\xf8\x05\x53\x6b\xb8\xf5\xb4\xcc\x13\x11\x63\xe1\xad\xe1\xce\xce\xf5\x69\x6f\x67\xeb\xde\x8b\xc5\xbd\xd1\xd6\x7f\x8d\xd1\x96\xa0\xf4\xbb\x08\x47\xb3\x9a\xef\xfe\x86\x96\x5a\x3c\x5a\x8e\x69\x82\xc5\xd3\xbe\x42\x10\x80\xe6\x6e\xfb\x83\x38\x1e\x58\x91\x2d\xe1\x3d\xae\x1d\x16\xc7\x75\xe6\x2f\x8d\xca\xdd\x08\x60\x15\x4e\xfc\x7d\x21\xc0\x3e\xf1\xcd\x4d\x38\x99\xc7\xc1\x6f\x57\x77\x00\xaf\x2b\x15\xbb\x03\xae\x95\x27\xdd\xae\x5a\x88\x9b\x17\xc0\xb1\x0e\xea\x94\xdf\x18\x46\x06\xc7\x15\x20\xe2\x13\x43\xdc\x49\x00\x02\xb6\x3f\xd8\x93\x61\xb8\xaa\x04\xbb\x00\xfd\x84\x0b\x9f\x2c\xb2\x69\x8e\x65\xdd\x1b\x84\x7c\x96\x32\xbe\x0e\x8f\x07\x7e\x20\x80\xd7\xf3\x47\x47\xd9\x34\xe7\x51\x06\xd6\x85\xf0\xd6\xac\xc3\x58\x18\xab\xec\x34\xee\xde\x0f\x0e\x29\xc9\x1c\x1c\xec\x4f\xbc\x6e\x74\x07\xe7\x1f\x9b\xed\xbb\xa0\x42\x4c\xec\x68\x3c\x34\x44\x44\x55\x48\x3f\x1c\x85\xd8\x17\xb7\x2a\xca\xc9\x38\xcd\x32\xd7\xa5\x24\x9c\x80\x82\x82\xee\x67\xd3\xdc\x17\xe5\x4f\xc7\x19\x7f\x48\xfe\x06\x27\xa8\x9c\x7c\x81\xf3\xd3\x35\x6b\x2f\x2a\xc4\x9b\x0e\xc3\xeb\xa4\x67\xaa\x70\x3b\xa5\x73\xa4\x0f\xb9\x1c\x0a\x50\xe4\xd8\x82\x04\x1a\xf1\xec\x8c\xcf\x1f\x02\x80\x42\xc8\x70\xe7\x02\x9b\x27\x38\x11\xd4\xd1\xbb\xd8\x56\x1b\xc0\xd3\xbf\x2c\xb8\xcc\xd7\x8c\xb9\x5b\xef\x38\xc1\x1f\x83\xae\x72\x49\xce\xce\xc5\x8e\x8a\xde\xba\x31\x10\xe0\xdc\xbd\x70\x25\xbc\xbe\x94\x30\xca\x58\x05\xac\x57\xb4\x70\x56\x97\xd8\x91\x84\xb8\xbe\xb7\x57\x46\xc8\xe6\xcb\x25\x76\xf6\x15\xf1\xd7\x2a\xa2\xa4\x75\x1c\x87\x02\x55\x2e\x78\xa5\x52\x06\x6c\x94\x30\xa9\x18\x41\x24\xd2\x77\x1c\xcc\x43\x5e\xce\xa6\xa1\x5d\x6f\x97\xb8\xe3\x0d\x62\xd5\xaa\x36\xd9\xaa\xa4\x3c\xd5\x7e\x25\xd9\x19\x61\x47\x57\x67\x18\xab\xf2\x0b\x33\x5a\x68\x49\x38\xd2\x6b\xcd\xcd\xf1\xf2\xe9\x78\x62\x83\x16\xa9\xdf\xf3\xbf\x11\x3b\x74\x8f\x94\x78\xf5\xf7\x39\x87\x17\xef\x56\xd0\x70\x8d\xa0\xa4\x15\xb6\x4f\x25\x51\x6b\x24\xea\x6f\x16\x9d\xc3\x5b\xbc\x72\xde\x6f\x14\xa3\x43\xb8\x17\xdf\xec\x91\xa7\x52\x1b\x54\xd1\xc4\x32\x59\x04\xe3\xcf\x47\x5c\x0f\x6d\x58\x01\x42\x92\xa1\x1b\x32\x93\x74\x17\x4c\x37\x4b\xb2\x2a\xfe\x43\x91\xde\x1e\xd9\x26\xcf\x64\xa2\xf4\x80\x4e\xe4\x39\x50\xbb\x04\x50\x7e\xcb\xcb\x1c\xa0\x63\x21\xa7\x27\x8a\x9b\x33\x2a\x74\x29\xd8\x7d\xb3\x8a\x7d\x77\xb2\x79\x4a\x86\x3e\x27\xdd\x07\x10\xfb\x39\x40\xe1\xb6\x25\xb2\xec\x80\xde\x41\x1c\xe3\xc5\xdd\xef\xf7\xe5\xfa\x3e\xb0\xcb\x5a\x9b\x8f\xe3\x1e\xe7\x90\x6f\x77\x10\xd6\x57\x82\xb2\xdd\x28\x50\x35\xf4\xcc\xb8\xfd\x32\x99\xfb\x82\x83\xb7\x87\xf2\xd0\x15\x18\xaf\xc3\x82\x24\x34\x7d\xa8\x48\x30\x1e\xf8\x9a\x9f\x8c\x58\x1d\x3c\x68\x20\x03\x17\x68\xf3\xd2\xae\x98\x55\x88\xdb\x5b\x47\xb5\xd0\xab\xb2\xd8\xc8\xab\x04\x3e\xf6\xef\x9b\x52\x06\xb3\xac\x25\xd5\x1e\x03\x07\x19\x2d\xff\x09\x97\xc9\x86\x58\x88\xd9\x0f\xb8\x7d\x36\xa5\x2f\x5c\x04\x8b\x3f\x76\x31\xad\xd0\xe7\xae\x9a\x25\x97\x96\x70\xfa\x5a\x7e\xdd\xf7\x14\x57\xeb\x68\x15\xe3\xa3\xc5\x8c\x23\x41\x54\xdd\x33\xba\xe6\x3e\xbc\x83\x52\x78\x09\x77\x8c\xf5\x80\x9c\x8d\x3b\x4f\x65\x9b\x34\xd8\x73\x1d\xdc\xb8\x3c\x00\xb9\xb7\x91\x2f\x2a\x0c\x27\x08\x3d\x6e\x5c\xb1\x6b\x3a\x15\xe6\x9d\xa6\xa1\xe3\x40\xbd\xc8\x2e\xad\x77\x83\x08\x14\x9e\x0a\x96\x8f\x97\x18\x6f\x1b\xc7\xf0\xb8\xbc\xe3\xb8\xa8\xe1\x14\xbf\x47\xa8\xd7\x39\x8e\xdd\x79\xd9\x3a\x92\x64\x2a\x37\x8a\x26\xe7\x4a\x7b\xdb\x30\x8b\xd4\xee\x0a\x56\x0b\x7f\xaa\xa5\x56\xbb\x66\x24\x49\x09\x40\x61\x8e\xf9\x03\xd9\x84\x43\x8d\x71\xd6\x74\xa5\x43\x1c\x31\x34\x48\xf8\x3b\xed\x24\x14\xbe\x1c\x21\xe4\x6b\xf2\x48\x1e\x54\x9d\xd8\xb7\x35\xcb\xd5\x88\xf7\xc6\xd6\x8d\x35\x0f\x1d\xf3\x16\x4f\x54\x57\x0b\xde\xdc\xd1\x3e\xcd\x8b\x68\x1e\x14\xf4\xa7\x00\x14\x88\x75\x54\x85\xc0\xeb\x28\x0a\xd7\x7c\x17\xd4\xf4\xf5\xa9\xa3\xd9\x0c\xa1\x71\xd5\xcd\x8e\x07\xb4\x6c\x66\xde\xcb\x66\xa8\x0c\x1d\x06\x21\x50\xa4\x2e\x50\xc8\x07\x78\x2a\xa6\xb4\x78\x61\x87\xf6\x91\x3b\xab\x5d\x4d\xdd\x5c\x89\xba\xee\x78\x9e\x1a\x21\x5e\xde\xe8\x8a\x95\x59\x11\xa9\xdf\x2b\x35\xdf\x22\x00\x21\x2e\x2a\xf1\x8c\xc8\xbe\x12\x61\xbf\x6d\x34\x42\x55\xff\x8d\x02\x12\xaa\x42\xab\x0e\xf2\x6b\x46\x27\xd4\x3a\x1a\x36\xc0\x6c\x31\x96\x6e\xaf\x72\x7e\x6a\xae\x63\x44\x02\xba\xdc\x2a\xa5\x62\x5c\xa2\xec\x1f\x9b\x2b\x11\x23\x4a\x8b\x04\xc3\x62\x8a\x11\x6c\x04\xcf\x89\xeb\x27\xce\xd2\xb8\x3e\x03\xe7\xb3\x9f\x58\x8f\xdb\x64\xc8\x3f\xac\x9d\xa4\xdd\x73\x84\x97\xa1\xf6\xcf\xa6\xf2\x94\x67\x39\x31\x9c\x53\x9d\xc5\x3b\x2e\xfd\x96\x72\x06\x59\x4b\x0c\x32\xac\x4a\xd9\xf6\xa3\x02\x16\x55\x6f\x3d\x9e\xd8\x42\x78\x82\x0b\x43\xd0\x59\x37\xb1\xa3\xad\x71\x60\x9b\x2f\xb0\x0c\x25\x7d\x73\xe8\xb4\xb2\xad\xc2\x42\x67\x3f\x58\x2c\xe2\x4b\xe1\x29\xa8\x11\x61\x75\x6d\x33\x36\xbe\x05\x58\xcd\xb0\xc4\x1b\xd5\x5d\x33\x0f\x22\xfe\x8e\x66\x3c\x3a\x04\xcf\xad\x63\xef\x78\x26\xec\x6b\x85\xdf\x91\xe9\x7a\xc5\x63\x57\x49\xa5\xe0\xe2\xb0\xa9\x31\x5c\x06\xe8\x4a\xcd\xde\xc9\x2f\x2b\x6e\x8a\x48\x7c\x24\x3a\xa9\xb4\x98\xde\xad\xa5\x8b\x1f\xf6\xf9\xa7\x8c\x3d\x24\xcb\x02\x81\x47\xd9\x78\x19\x07\xd9\xfa\xfa\xfa\x7a\x75\xc4\x21\x49\x41\xbb\x77\x12\x73\x88\x6b\x7f\x5b\xc3\xed\x27\x7e\x07\x2e\xdb\xf7\xb7\xff\xf7\xb7\xff\x7f\xed\xdb\x7f\x71\xf5\xcf\x60\x65\x4c\x28\x7f\x24\x8b\xdf\x2d\x46\x85\xcf\xb2\xa0\xda\x10\x60\x6d\x30\x80\x98\x57\x41\xc6\x48\x99\xed\x60\xcb\xdc\x1c\x22\x23\xb8\x30\x9a\x4c\x68\x46\x93\x82\xd0\xe4\x2c\x87\x42\xa3\x2c\x3d\xcf\x69\xb6\x86\x1c\x7a\x9e\x47\x49\x98\x9e\x83\xc6\x02\x45\x7a\x20\x0f\x1e\x88\x9c\xfe\x3f\xdf\xbc\x7e\x55\x14\x0b\xe1\x2b\x96\x73\x4d\x33\x8d\xec\xf9\x61\x81\xf5\x89\x40\x05\xd1\x34\x49\x19\x23\x88\xa3\x84\xb2\x9e\x24\x69\x48\xd7\x90\x77\x30\xa7\x46\x35\xf0\x8b\x79\xcc\x46\x26\x36\xb6\x76\xb7\x69\x23\xd7\x1c\x93\xff\x7c\xf5\x7e\xdb\xa8\x6e\x96\x6d\xb7\xbb\xa5\xa5\xa4\xe4\xc0\x5a\x78\x27\x91\xe9\x9a\x44\x80\xfc\xc4\x44\x7b\x70\x8f\xc9\x9d\x69\xb3\x5e\x2a\x03\x08\xa3\x3c\xde\xf2\x67\x69\x5e\xf4\x48\x11\xcd\x69\xba\x2c\x7a\xac\xc2\xac\x07\x4a\xe6\xf3\x34\x13\x8f\xd1\x60\x33\x61\x70\x64\x8f\xc0\x7f\x57\x57\xa4\x2d\x88\x3d\x4e\xc7\x41\xcc\x12\x87\x4f\xbf\x79\xfc\x0d\x04\x96\xe5\x7b\x0f\xaf\x90\xed\x84\xe2\xd7\xd5\x15\xd9\x54\xd9\xac\x19\xb2\x07\xad\xa9\x34\xd9\x28\xd9\x53\xed\xd7\x0a\x4f\x8b\x8c\x2e\x20\x52\x1b\x3d\xb7\xa6\xcc\x92\x9d\x04\xe0\x7b\x74\x96\x11\x92\xd3\xf3\x34\x8d\x69\x90\x5c\xc3\x1d\x2b\xdb\x9f\xa5\x04\xa3\xb1\x2c\xdc\x32\xa2\x03\x9f\xd9\x96\xe1\xfb\x07\x63\x1a\xc9\x5d\x66\x07\xcc\x8b\x40\x56\x3d\x47\x35\xbf\x41\xe1\x84\xc4\x78\x18\xdc\x00\xea\x6c\x42\xb4\x78\x05\x43\x7e\xf5\x7e\x5b\xc7\x75\xe5\x92\x16\xc2\x3c\x9a\x08\x06\x63\x38\xbf\xb3\x2a\x32\xc6\xc3\xab\x04\x79\x58\xd6\x9a\x2e\x68\xd2\x69\xbf\x3b\xfa\xf0\x51\x86\xa2\xe4\x84\xc3\x3b\xb7\xbb\x86\x3c\xe9\xc1\xdc\x3e\x78\x60\x4e\xaa\x71\xe8\x5b\x82\x41\x4d\xfb\x79\x90\x47\x63\xd2\x26\x1b\xd0\x85\xe7\x4b\xc6\x1e\x50\x15\x1b\xa4\x3d\x54\x57\x85\xaa\x9e\x7e\x91\x8a\x47\x6b\xed\x51\x90\xd3\x27\x8f\xdb\xd6\xf8\xb5\x1f\xe9\x57\x34\x08\x69\xd6\x69\xef\x03\x5f\x8d\x7e\x0d\xf8\x69\x0b\xda\xe7\x23\xac\x28\xc4\xe4\x63\x9a\x14\x8f\xd8\x41\xbb\xdd\x23\x6d\x26\xf9\x47\x63\xa8\x62\xf0\x4b\x2e\xd5\x8e\xea\xc6\x4a\x4c\x59\x0d\xb9\xf2\x88\x23\x97\xc9\x18\x1d\xaa\x6d\x4d\xb2\xef\xe2\x79\x81\xae\xaf\xfd\xb1\xa5\xab\x48\x2f\xb7\x63\x0d\x4a\x5d\x9a\x4d\x72\x92\x66\x4c\x5a\x15\xc1\x8a\x81\x1e\xb5\x76\x5f\x63\x2e\x09\x3b\xf0\x20\x82\x47\x91\x89\x26\x97\xaa\x7e\x81\x64\xa9\xc8\xc7\x6e\xa2\x7d\xd6\x00\x07\x69\x92\x50\xf1\x6c\x41\x52\x98\xa6\x44\xe3\x72\x51\xb6\x2e\x03\x36\x7c\xa4\x17\x85\xd3\x41\x01\x8b\x5e\x6b\x08\xeb\x78\xb3\x5b\x55\x5d\x7a\x2f\xea\xef\xf8\x1a\xc4\xab\xa4\x79\xec\x60\xa0\x81\xa0\x86\x08\xf6\x15\xc7\xa9\xa0\x04\x91\xf5\xa3\x13\xad\x83\x14\x59\x34\x9d\xd2\x8c\x87\x18\x62\xb3\x0f\x62\x8b\xf2\x17\xca\x70\x50\x47\x30\xd0\x03\x1f\xd5\x98\x91\x82\x9b\xd0\x0f\x18\xaf\xec\x1a\xdc\x24\x01\xdf\xce\x79\x11\x14\x74\x3c\x0b\x92\xa9\x5f\x81\xc0\xcd\xfb\x25\xe2\x83\xf0\x12\x0c\xeb\xe1\x46\xf8\x31\xe3\x30\x36\xcb\x5b\x37\x23\xfd\x36\xa0\x18\x0d\x28\x6f\x95\x50\x08\x29\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\x6d\x29\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\x23\x76\x4e\x3b\x8f\xd8\xd9\x23\x80\xa8\x60\xe5\xab\x09\xb5\xe9\x99\x0b\x8d\x4f\xa5\xcf\x50\xd1\x27\xa2\x70\x48\xbe\x53\x7f\x52\xdf\xc7\x76\x9f\x6c\x31\x1e\x92\xf6\x76\x7f\x53\x29\x0f\xa5\xfe\xb1\x9d\xd0\xe2\x53\x1c\xe5\x05\x4d\xa2\x64\xaa\xb2\x95\xf6\xf0\xd4\x30\xe8\x92\x0a\xae\x8c\x87\x38\x73\xc9\x57\x5a\x15\xb2\x41\xea\x49\x70\xd4\x05\x78\xe8\x52\x55\x60\x9c\xf6\x99\x98\xdb\x1a\x3e\x65\xbf\x0c\xf9\xb9\x35\xdc\xfa\x96\x9d\xfc\x77\xee\x4f\xfe\xf7\x27\xff\xbf\xf8\xc9\x5f\x1b\xfe\xc3\xa3\xc5\x3b\x32\xfa\x57\x86\x9c\xf8\x54\x39\x8a\xa6\xdc\x06\xb7\xff\x0b\x3f\xa1\xf3\x7b\x90\xf0\x35\x9d\x98\x1b\x82\x8a\xf5\x78\x89\x1e\xce\x19\x1b\x27\x87\xe0\xec\xe2\x7c\xc6\x7a\xdf\x31\x0d\xb4\xbe\xe7\x85\xc9\x43\xb2\xed\xbe\xbc\x03\x8b\x3f\x26\xc5\x9b\xef\x0f\x89\xff\x45\x9c\x60\xee\xef\xc4\xa9\x2e\x48\xc8\xe1\xf3\xfd\xb7\x62\x92\x43\xf2\xdd\xb7\x64\x9c\xce\x17\x4b\x11\x67\x65\x74\x49\xe6\xe9\x59\x94\x4c\x51\x34\xb1\xc7\x64\x3c\x0b\x32\xd8\x0b\xf8\xcd\x6c\xc8\x4d\xa9\xa4\xb9\xba\x84\x8e\x29\x7f\xb4\x50\xa4\xac\x41\x8e\xab\x9c\x74\xf6\xc9\x1e\xd9\xda\xec\x91\xe7\xec\xff\xad\x1e\xe9\xf7\xfb\x3d\xf2\x7f\x64\x8f\xec\x7c\xd3\x65\x87\x1d\x92\x2f\xe8\x38\x9a\x44\x7c\x21\x1d\x7e\x38\xda\xda\x79\xb2\xf5\xc4\x36\x31\x8b\xf2\x14\xd2\xc5\x38\x5c\xaf\xb2\xd7\xfc\x4d\x2c\xeb\x08\x1b\xa0\x79\xb5\x86\x6f\x96\x85\x24\x15\x4a\x30\xe1\xda\xc0\xac\xdf\x98\x50\x56\x31\x9e\x47\x36\xa2\xf6\x7e\xbb\xcf\xd0\x72\x90\x86\x74\xbf\xe8\x6c\x22\xad\x35\x1b\x5b\xfb\xff\x9c\x6c\xce\x00\xf9\xbb\x5d\x20\xd6\x22\x3d\x5e\x2c\x68\x76\x10\xe4\x5a\x95\x8d\xb2\xf3\xe5\x28\x2f\xb2\xce\xe3\xae\x7c\x91\x2b\x12\x36\x7b\x8f\xad\x1b\x33\x9e\xbb\x88\xa3\xa2\xd3\x6e\x77\xcd\xc7\xca\x49\xd7\xb4\xae\x1a\xa7\x21\x1b\x5c\xe2\xeb\xbc\x94\x0f\x01\xe6\x87\x3d\xb2\xcf\x04\x42\xf8\xf8\x7e\x8f\xfc\x5f\xd7\x89\x01\xe0\x99\x59\x31\xb1\x06\xa4\x72\x2a\x1b\x52\xf2\x88\xec\x93\x0d\xb2\xb5\x89\xec\x8c\x7c\x7e\xf1\x65\xec\x51\xdb\x86\xe9\xba\xdb\xff\x25\x8d\x12\x36\x4c\xdb\x52\x71\xbc\x04\x8f\xbb\x30\xc5\x6f\x8e\x5e\x30\xc2\xde\xda\x94\x4c\x49\x58\xf8\x01\xe5\x7b\x28\xee\xdb\xcd\x27\x8f\x6d\x82\x9b\xa7\xe1\x77\xdf\x6e\x6d\x96\x11\x9a\x49\x5f\xda\x8f\x31\xa7\x26\x51\xb8\x92\x8a\x32\x3a\x0f\xa2\x84\xeb\x8e\x58\x9e\xbe\x7b\x14\xce\x5d\x4c\xf6\x20\x80\xb5\xdd\xf2\x76\xd7\x72\x5b\x03\xcc\x4a\x82\x29\x8b\xd7\xef\x0c\x13\x39\xdd\x24\xc8\xda\x87\x49\xc1\x3d\xe2\xf4\xc8\xd6\x66\x97\xfc\xff\x19\xd6\x36\x9c\x5a\xb8\x53\x1c\x61\x7e\xee\x7b\x81\xab\xea\x52\x25\x75\x7d\xc6\x3c\xd5\xbf\x43\xe2\x26\xe8\xb0\x0e\x84\xc1\x3f\x5c\xa8\x43\x82\x78\xeb\x20\xd8\xa7\x9c\x2f\xff\xe4\x0c\xb0\xb7\x6b\xff\x24\x08\x4b\x68\xbd\xe4\xdc\xae\x3a\x51\x66\xeb\xfa\x49\x21\x08\xd1\x72\x2e\x5f\xe7\x58\x44\xc5\x60\xf6\x55\x8e\xd3\xf7\x00\x65\x49\x31\x9a\x0d\xe1\x5a\xb1\x35\xac\x15\x63\x39\x7d\x54\x63\x9d\xcf\x80\x20\x7f\x2e\x7d\x06\xa0\x97\x0a\x22\x1a\x28\xd9\x7a\x82\x58\xd8\x28\xc8\xe9\xce\x13\xb2\x07\x65\xb4\x7a\x68\xe7\x89\x61\x02\x10\x86\x94\x6b\x16\x61\x0f\xec\xf0\x42\x3d\xb2\xf5\x8d\x29\x09\xab\x7e\x3e\x1f\x05\x49\x87\x17\x33\x99\x9f\xb5\x98\x85\x5b\x12\xb4\x70\x9f\xb3\xa1\x17\xa9\xb1\x7b\xb1\xe9\x23\xe0\x5a\x35\xbb\x94\x2b\x9a\x2b\x93\xc0\x5e\xf7\x1d\x8f\x05\x91\xa4\x85\x10\xca\xbe\x8f\x7e\x68\x4d\x41\x22\xe1\xee\x6e\x26\x1a\xa9\xf9\x2c\xe0\xd2\x1a\xec\x6f\x17\xe3\x78\x99\x47\x67\x2a\x74\x65\x34\x8a\xe2\xa8\x50\x02\xce\x28\x48\x3e\x0f\x46\x59\x90\x8c\x67\x24\xa7\xd9\x59\x34\x96\x1b\x60\xc0\x3d\xbd\xb6\xbe\x1f\x44\x3f\xf4\x6d\x1a\x52\x61\x24\x72\xb9\x0b\x4d\x68\xc6\xb6\xa1\x20\x9e\xa6\x59\x54\xcc\xe6\x24\xa4\xf9\x38\x8b\x46\x9c\x2d\x09\xf9\x87\x26\xfd\xf3\xe8\x73\xb4\xa0\x61\x14\x80\x10\xc4\xbe\x06\x87\x49\x41\xb3\x24\xe0\x4f\x27\x3e\x3d\x0f\x92\xcf\x9f\x84\x9b\xd9\x4f\x7c\x5e\xff\x7f\x3f\x89\x91\x26\xd3\x4f\x6c\x88\x9f\xe0\x2d\xd1\xa7\x30\x9a\x46\xce\x53\x0e\x39\x35\x3e\x8a\x1c\xc9\x3d\x55\xce\x80\xf4\x19\x53\xa4\x9e\x6d\xb6\x01\xad\x3e\xb7\x57\xe4\xc8\x62\x8b\x62\x46\x0f\xf8\x3e\xd5\xfe\xe7\xcb\xf6\xee\x9a\x97\x67\x0a\x1e\xdb\xb1\x76\xee\x0e\xae\x60\x83\xb4\x37\x41\x54\x82\x56\xb0\xb9\x0b\x43\xc7\x0b\x86\x0d\xb2\x47\x3a\x5c\x9c\xea\x7c\xf7\x94\x3c\xd2\x4d\x74\xe5\xb3\x81\x47\xdb\xd6\x7e\xab\xbc\x6e\x98\x4d\xa1\x3a\x45\x83\x35\x6a\x2b\xc1\x44\x10\xae\x80\xb0\x79\x00\xf1\x28\xc9\x8b\xa8\x58\x16\xd2\x59\x72\x14\xd2\xa4\x60\x9b\x96\xed\x78\x9f\xd7\x72\x98\x84\x51\x46\x4d\x03\x06\xf3\x8d\x4d\xde\x93\xb2\xac\x7a\x64\x03\xaf\xa6\x5a\xa8\xa5\x16\x34\xd5\xd2\x6d\xb5\x56\xe1\x45\x66\x4f\xbc\x0e\x94\xcd\x23\xb0\xc9\x19\xda\x2f\x3f\xbe\x62\xf3\x20\x5f\xb7\x60\x0c\xa0\x54\xd5\xb7\xae\xc5\xaf\xd3\x2a\x7e\x2d\x9f\xd2\x71\xe4\x8a\xe8\xdc\x51\xce\x5f\xca\x61\x3e\xee\xc8\x9d\xe0\x41\xa5\x54\xde\x54\x7b\x91\x47\xf1\x21\x15\x1e\xfc\x39\x1d\x6f\x49\x09\x9d\x87\xc8\x3f\x4b\xa5\x9c\x10\x61\x3f\x2f\x11\x27\x2b\x2c\xfc\x69\x27\x2f\xb5\xba\x72\x85\x05\xe8\x7a\xe9\xeb\x41\x3c\x66\x1d\x95\xc1\x3b\xaa\x1e\x49\x3d\x5a\x1b\x18\x1b\xd6\xd6\xb8\xa3\xb4\x28\x61\xf0\x9f\x7f\xbe\x3c\xd9\x7c\xf4\xdd\xe9\x97\xed\xeb\xce\xcb\x8f\xaf\xd8\xef\xfd\x47\xff\x77\xfa\x65\x6b\xe7\xfa\x4a\x7d\xec\x6c\xf6\x76\xb6\xae\xbb\xff\x33\xe8\x17\xa0\x04\x55\x1b\xb8\xf1\x2e\xaf\x8c\x31\x20\x70\xfe\x3c\x6f\x6b\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\xec\x3c\x86\x38\xff\xfe\xbc\x6f\x6e\x38\xec\x09\x89\x92\x92\x81\x1b\xdc\xe7\x6e\x86\xee\x65\x23\x8d\x06\xbf\xbd\xd9\xc8\x6a\x93\x8b\x94\x6c\xa4\xf9\x72\xce\x00\x8f\x73\x71\x7c\x98\xa7\xe1\xa3\xef\xbe\x7d\xb4\xb5\xa9\xb2\xe1\x8c\x0b\xbd\x1b\xa7\x31\xe9\x1c\x7e\x38\x1a\x1c\xbe\x3c\x20\xec\xdc\x30\xdc\xde\xdc\xdc\xe9\xda\x3c\x19\x55\xeb\x9e\x42\x51\xae\x33\x70\x99\xd7\x70\xd8\xe2\x4c\xb8\xdd\x23\xdb\xcd\x6c\x55\x31\x53\x35\xb6\x14\x42\xa7\x7d\xf2\xcf\xf7\x2f\x7f\x72\x1c\x09\xaa\x02\xfe\xd1\x94\xd6\xe8\x4e\x2a\x82\xac\x1b\x9e\x26\x80\x0e\x78\x99\x73\x86\xfc\x6d\x8f\x3c\xee\x92\x21\x69\xb7\x1b\x8d\x7b\x1c\x47\xf0\x90\x4c\x75\x10\x94\x4f\x51\x62\x8f\x8f\x61\xe1\xa7\xfd\x7f\x1c\xfd\xf8\xaf\xa3\xf7\xff\x6b\xcf\x2a\xd4\x51\x32\xa7\x76\xfd\xde\xc9\xe5\x40\xb7\x1e\xfb\xd6\xd6\xea\x23\x17\xab\xc9\x7f\x2e\x71\x0f\x1e\xee\xd0\x9c\x0a\x9c\xe1\x05\x9e\x73\x08\xbe\x77\x12\x83\xf3\x39\x20\x33\x0e\x1d\xee\x80\x1f\xa3\x43\x6c\xe9\x51\x46\x9e\x3f\xd4\x29\xc5\x38\xa1\xf2\x33\x8a\x79\x9e\xd9\x7a\xd2\xed\x91\xed\x4d\xe5\xe2\xcc\x90\xf2\x24\x7a\xad\x41\xca\xc2\xcd\x16\x68\x89\x57\xaa\x43\xc8\xe2\x4a\x7d\xac\x57\x6c\x0d\xcd\xcf\xeb\xd3\xde\xce\xe3\x7b\x35\xfe\xbd\x1a\xff\x2f\xae\xc6\x17\x2a\xfc\xc5\xb8\xda\x7e\xef\x16\x16\x77\x2d\x1d\xa2\xb0\xb5\xbb\x52\x68\xb5\x1a\x3b\x3d\xae\x67\x5a\x8c\xbd\x96\x60\x8b\xa0\x98\xf5\x48\x42\x0d\xeb\xef\x4f\xa0\xb9\x70\x1e\x9e\xca\xab\x6a\x1c\xdc\x59\x7a\x2d\x10\xf6\x3a\x60\xe3\xc3\xfe\xe3\xa9\x3a\x6b\xac\x6e\x78\x81\x2b\x16\x32\xa1\xf3\x85\x41\x0f\x75\x79\xe5\x8a\xd2\x2a\xd6\x4f\x93\x4e\x1b\x46\xd5\xc6\xc1\x50\xbb\x86\xfd\x74\x9e\x32\x26\xc6\xdf\x12\x1e\xbe\x3b\x20\xfa\x5e\x99\xbf\x30\x6c\xf7\x08\x45\xac\xf7\x13\x67\x83\xe2\xc2\xbb\x63\x3b\xc3\xf4\xf6\x20\x09\x71\xfb\xa8\xf9\xd2\xca\xc8\x9a\x7a\x63\xf0\xfa\xf0\xc3\xc7\x97\x6f\x61\x05\x1d\x1c\xbd\x7d\xfb\xf2\xe0\xe3\xe1\xd1\x5b\xf2\xfe\xe5\x87\x77\x47\x6f\x3f\xbc\xfc\x50\xda\x6a\x18\x14\x01\x6e\x96\x7d\xe3\xcd\x69\xf0\x50\x98\x11\xce\x83\x8b\x71\x3a\x5f\xc4\xf4\x22\x2a\x2e\x87\xe4\x09\x50\x96\xd5\x43\xd0\x85\x2a\x3b\x04\x56\x95\xde\x6f\xba\x9e\xc8\x35\xc2\xe6\xe0\x8b\x19\xc8\x1a\x0e\x7e\xa1\x6d\x3b\x21\xba\xc3\x03\x7c\x03\x7f\x09\xc9\xf9\x2c\x1a\xcf\xc8\x3c\x28\xc6\x33\x21\xbe\xf2\x4d\x88\x31\xb4\xd0\x28\xe7\x89\x5a\x00\x4d\xfb\x23\x5d\xc3\x75\x94\xd3\x5b\xb0\x40\xf0\xc7\xa1\x8d\x26\x9d\x4f\x7e\x42\x3e\x81\xb7\x71\x51\x78\xea\xba\x43\x57\x85\xd9\x58\x05\xd8\xae\x03\x65\xc7\x0c\x2f\x8d\xa5\x0b\xd5\x88\xbe\xdb\x15\x5d\x3b\x58\x9c\x44\x19\x35\x3c\x02\xd8\xe8\x2a\x1b\x0f\x1b\x8a\xa7\xf5\x0a\x70\x1d\xd8\x17\x9b\xb6\xe8\xbf\x90\xc6\xb4\xa0\x55\x35\xd8\x83\xb1\x71\x83\x5f\x61\xff\xcc\x76\x2d\x20\x44\x41\x10\xbc\x3e\x50\xee\x70\x5b\xa9\x84\x3b\xcb\x21\x29\x77\xb5\x1c\x15\xfd\xb5\x35\x29\x0c\x9a\x24\xbc\x66\xab\x3d\xe0\x45\x26\x13\xfe\x34\xcf\x43\xe2\x91\x59\x18\x7b\x56\xc5\xab\xca\x66\x83\x3d\x4b\x5e\xfb\x07\xf7\x6c\xae\x1d\xf4\xca\x25\xfe\xe2\xe5\xa3\x83\x57\xc7\x6f\xff\xf7\xe5\x7b\x55\x4f\x48\xc7\xb3\x65\xf2\x99\x86\xe2\x55\x09\x7f\x31\x2a\xfe\xfa\x19\x5d\xc4\xc1\x98\x76\x06\xff\xbe\x3e\xf9\x77\xf2\xef\xec\xf4\xd9\xbf\xbf\x0c\xa6\xbd\xf6\xf5\xd5\xa3\x47\x57\x5f\xda\x5d\x70\x2d\xfc\xc5\x0b\xff\xef\x53\x59\xe2\x44\x94\x39\x65\x85\x4e\x64\xa9\xd3\x13\x7f\x39\xbb\x94\x51\xa8\xa4\x8c\x6e\x0b\xb5\xa4\x1a\x42\x65\xc4\x35\x1f\xcb\x6e\x4b\x4e\x6a\x60\xc0\x5d\xb3\x80\x78\xc4\x5f\x06\x03\xb8\x03\xa5\xc2\x1d\x06\x78\xda\x80\x0a\xd6\x1c\xd2\x67\x79\x07\x2c\xcb\x5c\xb9\xc2\xef\x8c\x05\x43\x36\x08\x7f\xff\x6a\x88\xea\xea\xce\xda\xe2\x64\xae\x53\x03\x9f\x2d\x18\xf4\x1d\x95\x12\xd6\x34\xdc\x98\x66\xcd\x5d\x7c\xba\x33\x7b\x76\x67\xc4\xd0\xc1\xe7\xae\xb2\xa0\x06\xd7\x77\xc9\x98\xc6\xe0\x50\x5f\x3e\xe2\x34\xca\x8c\x63\x1a\x64\xd2\x84\xcb\x6a\x45\x24\x5b\x0b\xda\x0f\x04\xbe\x1a\x0a\x59\x91\x6f\x8f\x33\xcb\xdb\x7b\x1d\xfe\xab\xb4\xab\x14\x38\xc3\xf0\xd7\x3d\xb2\xb5\xb9\xb9\x49\x1e\xf2\xcb\x19\xcf\x5d\xab\xd7\xf1\x03\xbc\xdb\x03\xec\x48\x7c\x31\x0e\x92\x53\x41\x2f\x3c\x16\x8b\x78\xd7\xb7\x3a\xaa\xdc\x19\xb3\x48\x04\xc2\xfd\x08\xcb\x4a\xa7\xc3\x9c\x45\xf0\x80\xf0\xa6\xdd\x9e\xa5\xad\xc7\xe0\xc2\xf9\x0f\xe3\x91\x3f\x89\x2d\x34\x08\xc3\x1c\x47\x0a\x17\x56\x0e\xae\x34\xc6\xd5\xc3\xbd\x35\xbe\xe1\xca\x83\x81\x38\x6b\x47\xdc\x5d\xbd\xe0\x7a\xb0\x1b\xcb\x5b\x21\x95\x7a\x18\xf2\x52\x41\x96\x45\x67\x14\x33\xdc\x20\x54\xb3\x27\xdb\xab\xe0\xb0\x1e\x68\xc3\x5b\xbd\xdf\xa6\x14\xc9\x14\xf2\xb5\x7a\x14\x92\xab\x2b\xf9\x75\xb2\x79\xaa\xb6\x4c\xb8\xc2\xe6\x7d\xd3\xd0\x22\xc1\x2c\xc1\x13\xb1\x44\xe7\xdd\xbc\xc8\x9e\xea\x4d\x95\xc4\xcb\x40\xfb\xaa\x61\x59\xb7\xdc\xd5\xe4\x3a\xc2\x2b\x95\x9c\xcf\xa8\xf4\x3b\x10\x72\xb1\x1c\x4e\x5f\xa0\x71\x67\xfb\x7b\x88\xd0\x2c\x88\xb8\x02\xb5\xae\x7d\xa7\x3a\xda\x4f\xd2\xac\xc3\xf0\xf2\x99\x5e\xf2\x93\xa2\x6f\x00\xa6\x13\x98\x8e\x1f\xa8\x3f\x0b\xf2\xa3\xf3\xe4\x1d\x04\x5a\x2a\x2e\x21\x76\xa1\xc5\x05\x4a\xd0\xf3\x99\x5e\x9e\x96\xdb\x76\xb6\xd3\x84\x1c\xbe\x3b\x68\xdb\x41\xfc\x85\x6c\x51\x51\xa7\x63\x66\xa1\x97\xc9\x01\xf6\x41\x28\xdc\x8d\x13\x74\xdc\x88\x72\x92\x17\x11\x0f\x46\x12\x85\x88\xa8\xb1\x59\x68\x29\xc2\xfd\x76\x9c\x9d\xf2\xd3\x92\x94\x03\xd8\xee\x91\x51\xd1\x8f\x1e\xa7\x02\xb3\x57\xd3\x34\xa1\x42\xf3\xd4\x59\xff\x64\x8b\xfd\xe7\x59\x54\x80\xbf\x14\x8b\x1b\x21\x10\xeb\x08\xf5\xc9\x3d\x43\x49\x17\x83\xeb\x65\xb5\x0b\x05\x92\x77\xe8\x55\x2f\x08\xd6\x30\xfd\x58\xf5\xd2\x0f\xe8\xe9\x0a\x31\x36\xd9\x5d\x83\x73\xaf\x80\x22\x89\xa6\x7a\x2c\x11\xcf\x11\xaa\xf6\xac\x29\x7b\x19\xa2\x67\xbf\xbe\x51\x55\x58\x3c\xdf\x4c\x6c\x50\x54\x8d\xa5\x06\x73\x28\xb5\xfb\x28\xb1\xfe\x7c\xfb\xa4\x65\x76\x27\xb4\x89\xd6\x19\xc5\x71\xc7\xf3\xaf\x74\x09\x56\xd6\xfa\xb5\x59\xab\xbd\x61\xb3\xdb\x8d\x76\x8b\xe4\xd8\x30\xbb\x8f\xed\xb4\x35\x1f\x84\x17\x5b\x69\x41\xf2\xe5\x62\x91\x66\x05\xe8\xd6\xf8\x4d\xed\xbb\x03\xa2\xb4\x2a\x6d\xc3\x11\x64\x39\x61\x36\x7e\xa9\x70\x93\xc5\x58\x4f\x65\x2b\x51\x98\xf7\x58\x0f\x34\x55\x69\x41\x8f\x1c\xea\xda\xbb\x69\xa9\xb7\x1b\x57\x8f\xab\x31\xe8\x38\x69\x2f\x79\xa5\x7d\x7d\xda\xdb\xf9\xe6\x5e\xa5\x7b\xaf\xd2\xfd\xaf\x50\xe9\x8a\x87\x15\xb7\x7a\x8e\xbd\x1f\x64\x69\x42\xfe\x77\x39\x0f\xce\xa2\x9c\x7c\x1f\xb0\xcf\xbf\x7d\xe6\x9f\xfd\x39\xf5\xaa\x7b\x07\x03\x72\x98\x44\x45\x14\xc4\xd1\xaf\x94\xfc\x9d\xf7\x82\x11\x6a\x40\x72\xb0\xc4\x92\x06\x37\x30\x50\xb6\x54\x0d\x27\xe7\x7d\xd0\xea\xca\x62\x32\x8a\x88\x08\x40\x75\x18\x0e\xc9\x66\xdd\xcd\x1b\xb7\xf6\x60\xc3\xb7\xdd\xea\x7a\xcd\x4c\xbc\xee\x74\xf5\x2b\x34\x19\xeb\x6a\x22\x11\x0a\x2d\x69\x83\x1e\x8f\x13\x5e\xfe\x3a\xa5\x87\x54\x3d\x13\x59\x8d\xcc\x92\xbe\x77\xbd\x6e\x88\xd0\x08\x58\x7b\x4e\xef\x07\x6b\x02\x3d\x25\xae\x78\x79\x5b\x3d\xd1\x98\xe1\x34\x95\x67\x75\xcb\x54\xcb\xb2\x49\xc7\x98\x47\x99\xed\xae\xb7\x51\xd4\xa9\x20\x3c\x63\x67\x54\x39\x3b\xe4\xf0\x05\xe4\xc8\xde\xa9\x49\xdb\xd8\x28\xf3\x33\xe4\x7f\xfd\xc3\xdf\x0a\x39\xd5\xe8\x6c\xf9\x3c\x48\x8c\x54\xa5\xcb\x77\x41\xfc\x7f\x76\x60\x92\x2f\x84\x9a\x1b\x5e\x48\x1c\xa8\xc3\xa3\x34\x20\xf2\x9b\xea\x28\x65\x5d\x5d\xe4\x33\xcf\xcb\x6c\xab\x01\xbf\x79\x86\x44\x83\xd5\x9e\x15\x10\x99\x27\x5a\x97\xa1\xdc\xa7\x0f\xd2\x39\x0b\xa0\x67\xaa\xed\x3e\x3d\xa3\xd9\x65\x47\x7a\x43\xfe\x10\x25\xd3\x98\xbe\xe1\x08\xef\x92\x21\xf1\x66\xe8\x9a\xc4\xb4\xaa\x8e\xf8\xc1\xc5\x04\xaa\x83\x96\x12\xde\x25\xdd\x20\x0b\x22\x99\xc6\x29\xd2\xb0\x2d\x12\x19\x72\x7e\xf6\xf6\xf6\x38\xd5\x60\x20\xe1\x76\x41\xc2\xb2\x33\x37\x03\xe3\xd7\xba\x6d\x5f\x75\x42\x86\xb5\x7c\x4a\x0e\x06\x3c\x34\x9f\x4a\x12\x5e\xd9\x31\x73\x91\xeb\xb1\x91\x3f\x79\xce\x88\x46\xf0\x1e\xad\x86\x1d\x3d\x67\x40\xe5\x2e\xbe\x45\xc7\x2d\xfe\xc2\xeb\xca\x39\x53\x15\x55\x49\x01\x27\xec\x82\xf2\x48\x2c\x8a\x8e\xe4\x3d\x5d\x32\x89\x68\x1c\x5a\xa6\x07\xa2\x15\xa3\xa7\x16\xcf\xc1\x1d\xb4\x18\x0f\xef\x9a\x45\x86\x32\xd9\x8a\xfa\x20\xc9\xc2\x75\x84\xe5\xb0\x37\x09\xdb\x97\xac\x4d\x7e\x0b\x16\x67\xea\xe1\x1d\x59\x51\xd4\x27\xe4\x44\x26\x06\x3e\xb9\x17\x03\xef\xc5\xc0\xbf\xb6\x18\xa8\xdf\xe7\xf1\x45\x73\x57\x2f\xf4\xee\xe6\xee\x9e\x81\xbc\x91\xea\xc6\x52\x63\x65\x38\x27\x8a\x48\x2d\xd2\x0a\x99\x7d\xa2\x53\xa4\x70\xb9\x26\x73\xd9\xa7\x71\x71\x0f\x3c\x4f\xe7\x6b\xc9\x60\x13\x81\x81\x4f\x7e\x1c\x94\x50\x1b\x42\xe3\x0c\x54\x82\x7b\x7a\xf6\x15\xb1\x72\x0c\xa5\x2b\x68\x0c\xde\x04\x49\x30\xa5\xfa\x75\x3e\x63\x59\x1c\x15\x86\x2a\x40\xba\xf0\xd0\xe0\x68\xbf\x9f\x1b\x18\x72\x2a\xce\xe6\x35\xf6\xef\x21\x65\x1c\x26\x4a\x4c\xff\x9e\x96\xf8\x37\x0a\x72\xee\x73\xa1\x2c\x12\xc5\x94\x82\x97\x4a\xcf\x26\x65\x7a\x9a\xb7\x1d\x8b\xca\x36\xcd\xf6\x80\xc4\x1c\x44\x88\x36\x4a\x63\x4d\x18\xee\x44\x51\xf8\x1c\x45\x1c\xca\x8e\x4f\xfa\x32\xcc\x99\x60\xa3\x52\xea\xdc\x1c\x73\x67\x9c\xfa\x92\x42\x84\xe6\x10\xdb\xae\x1a\x67\x9f\xbc\x61\xac\x3c\xa2\xb9\x08\x22\x0d\xf8\x70\xbc\x50\x1a\x9e\x3d\x1b\xe3\x4d\x0e\xea\xea\xed\x32\x8e\xb5\x63\x8c\x1e\x93\x22\xe9\x45\x04\xd7\x66\x3e\xdc\xfd\x31\xe3\x0f\xdd\x59\xd8\x1d\xb2\xf6\xb5\xe2\xee\x38\x98\x6c\x14\x6d\xc7\x0e\x70\xa2\x42\xc9\x98\x07\x31\x52\x13\x3e\xe6\xfd\xbb\x03\x11\x61\xa2\x3a\x76\x8c\x46\x9b\x70\xf5\xca\x09\x0f\x90\xae\x4e\x9c\x36\x9a\x38\xe8\x21\x83\x74\xb1\x64\x10\x9d\x4a\xf2\xa0\x03\xd5\x52\x89\x8d\x75\x0f\x77\x2d\xa1\x20\xdf\xe3\x46\x4f\x69\x4b\x86\x54\x4e\x17\x7b\x04\x82\x64\x57\x85\x90\x22\xcf\xf4\x6f\x4e\xdd\x50\xe4\x94\xb1\x03\xf4\x59\xe3\x59\xdf\xc1\x3a\xe7\xf7\x2a\x7a\x2d\xc6\xbc\x8b\x78\xee\x80\xb7\xfa\xac\x68\xba\x23\x2e\xc1\xbd\x27\x46\x8a\x19\x2c\x17\xa3\xd0\xde\xac\xc0\xd9\x0c\x1c\x7b\x9e\x79\x01\x54\x55\xde\xd8\x24\x02\x17\xbe\x90\x45\xf2\xfd\x94\xa4\xc3\x15\x22\x17\x05\x72\xdd\x36\x42\x42\xb3\x18\x44\xd8\x1d\xab\xd8\x47\x6c\x2f\xc9\x2b\x3b\x5f\x16\xf2\x04\x00\xa3\x65\x80\x01\x21\xcf\x08\x30\xa4\x8e\x29\x7e\x2d\x88\x54\x67\x80\x66\xa9\x44\x99\x51\xe5\x56\x19\xab\x38\x1c\x54\x49\x17\xb9\x1c\x9f\xa6\xb4\x35\xfa\x05\xa3\x8b\x65\xc8\xa1\x8d\x96\x51\x1c\x02\xc2\xc4\xa0\x58\xa6\xe3\xdf\x16\x18\xfe\xc7\xa3\x17\x47\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\x96\x11\xe9\x69\xf4\xcb\x09\xcb\x3e\xd9\x3c\x3d\x65\x62\x17\xfe\xbc\xba\x52\x76\x9b\x36\x28\xff\xb1\x05\x65\xd8\x58\x76\xfd\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\x03\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\x70\x74\x8b\x9b\x3f\x9f\xde\x47\x69\x6b\xb0\xe2\x87\x33\x27\xa5\xd1\x79\xf1\xdc\xe0\x23\x6c\x18\xa6\xcb\x0f\x47\x53\x01\x23\xb5\x92\x77\x2b\x02\x85\xad\x69\x79\x29\xef\x78\x6c\xfa\xc5\x05\x17\xf9\x42\x2c\xe9\xca\x92\x83\x3a\xac\x66\xb4\xb3\x08\x20\x47\xed\xd2\xf1\x75\xd0\xd2\x37\xeb\x5d\xbe\x3c\x60\xd1\x62\x59\xa8\xc7\x6b\x09\x3d\x17\xd8\xec\xe8\xed\x92\x09\x1d\x43\xd2\x56\x70\x56\x1c\x8d\x21\x69\x87\xa3\x4f\xbe\x5c\x29\x26\xee\xa8\x3e\xa9\x46\xa7\xb4\x59\xa3\x0a\xce\xdb\xa8\x2f\x57\x36\xba\xed\x36\xba\x58\x16\xaf\xe8\x45\xfd\x30\x5f\xd1\x8b\xb2\x31\x9a\x59\xd5\x03\xac\x6f\x8b\x03\x95\x0d\xcd\xdf\x96\x35\x2e\xb1\x19\x9d\x68\x38\x39\x11\x3d\x8d\xe4\x9e\x18\x7a\x4f\x74\x0b\x80\x4f\x4b\x76\xae\x17\xcf\xf5\xae\xc5\x69\xa7\x35\xdc\x81\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\xfc\xc8\x43\x81\x5b\xc8\x80\x44\x09\x72\x98\x7f\xb8\x4c\xc6\x7c\x8b\xc1\x50\x39\x4f\x95\x60\x8c\x15\x67\xd4\x06\x12\xa9\xaa\x2e\xee\xa0\x08\x43\x44\xa3\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\x6e\x9e\x76\xc9\x83\x07\x44\xfc\xee\x83\x4e\xf0\x68\xd2\x69\x6f\x5e\xb4\xb9\xeb\x92\xcd\x2e\x79\x46\x5a\xb4\x98\xb1\xdd\x03\x02\x93\x3e\xbf\x7c\x15\xe4\xb3\x16\x19\xda\xc9\x5c\xa3\xdb\xd2\x52\x02\x8a\xff\xf4\x63\x96\xce\x9f\xff\x06\x3d\x6d\x8b\x2e\xa1\xb0\x42\xcf\x2f\xa1\x61\xd6\xe9\xfd\x24\x3c\x64\xe5\x54\x34\x2f\x2f\x24\x1f\x87\x82\xd5\xe3\x59\x26\xe3\x98\xfe\x46\x03\x38\x66\x6d\xd5\x74\x1d\xc3\x94\x76\x5a\xce\x0f\x1a\xe7\x41\xba\x4c\x1a\x5d\x33\xdd\xc1\x38\xbc\x6d\x73\x12\xc2\x43\x29\x01\xe3\xa3\x72\xa6\xe0\x37\xec\xff\xb1\x6a\x10\x4d\x86\x33\x09\x18\xc0\xe8\xb3\xea\xde\xcb\x62\x76\xd7\x07\x84\xc6\x87\x83\x3b\x3a\x1b\x40\x00\xe0\xf2\xb3\x01\x57\x7d\x70\x2e\x1e\x51\x6f\x8f\x16\xb8\x33\x8b\x9a\x7e\x2c\x6e\xd0\x05\x74\xc7\xcd\xb9\x2b\xf7\x7f\x41\xb0\x87\xee\xc3\xe7\xfb\x6f\xad\x60\x64\x82\xa7\x72\xad\x0c\x7f\x40\x2b\x74\x33\xd7\x6b\x6b\xbc\x77\x7d\x6e\x19\xa5\xde\xd2\xbc\x2c\x66\x5a\x1b\xd4\x23\x6d\x1c\xba\xb9\xdd\x13\xc3\x9c\xd2\x62\x58\xa2\xf3\x94\xbe\x4a\xfb\xb8\xa0\x18\x49\x4f\xe8\xe9\x8c\xc2\x67\x41\x6c\x44\x19\xeb\x5b\x81\xb3\xcf\x82\xd8\x71\x46\xa2\xd2\xae\xd7\x00\x3d\x2b\x0d\x45\xf8\xf9\xbb\xc9\x60\x44\xd1\x9b\x0c\x47\x14\x6d\x38\xa0\x26\x67\x51\xc6\x5d\x82\x18\x2c\x37\x6b\x4f\x4e\x02\xd0\x3d\x3d\x49\x36\xe5\xe4\xab\x23\x14\xb2\xe6\x34\xae\xf0\x86\xe4\x44\x0b\x54\xfc\x7a\x4f\xb8\xd1\xfc\x51\xdf\xe6\xd9\x10\x38\xf2\x39\xe7\x27\x0a\x18\x85\x8e\xb4\xee\xb1\x86\xb8\x1a\x9e\xa7\x7c\xd6\x28\xa0\x92\x63\x73\x9a\x05\x53\xba\x5f\x34\x39\x39\x0b\xd0\x52\x1c\xf9\x20\xd4\xa1\xb6\x02\x4b\x7c\xdd\x71\x8e\x5d\xa4\x70\xb2\x5c\x05\x2d\xde\x81\x09\xe7\x8e\x35\x63\x62\x50\xa5\xc3\xb1\x32\x7f\xfb\xf9\xf6\x0e\x4c\xae\xfa\x3a\x7a\xe6\xec\xc8\x1a\x9a\x12\x19\x6f\x37\x2c\x5f\x6f\x7b\xce\x12\xd7\xf6\xaf\x6c\xf1\x92\xeb\xd5\xe8\x97\x35\x51\x4d\xbb\xb0\xff\xd6\x63\x02\xc0\x1c\x4c\x28\x89\xee\x6b\x60\x02\x91\xf2\x2d\x06\xdd\x5b\x2b\xa1\xec\xf9\x22\x8a\xf9\xe1\xad\x96\xbc\x05\x68\x05\x8d\xbb\x10\x12\x0f\x9b\xe5\xf4\x67\xcb\x6b\x0d\xe9\xd1\x2e\xe6\x74\xab\x4a\x64\x75\x3b\xb8\x75\xcb\x89\xaa\x9a\x1b\x39\x85\x2f\xe8\x38\x9a\x07\x71\x39\x2a\xb4\x14\xd8\x10\x09\xba\x40\x09\x51\xfe\x71\x07\x6c\x0a\x4f\x35\x83\x2d\x8f\x95\x5c\x72\x04\x03\xf9\xba\x72\xd0\xf5\x2b\x08\x55\x58\xcd\x3c\x3e\x7a\x4e\xa8\x2b\x8d\x49\x95\x72\x06\x57\x76\xf8\xfd\x23\x71\x9a\x9b\xe0\xe9\x3d\x1d\xd3\x68\xd1\x80\xcc\xdd\x32\x4d\x08\xc0\x05\xbd\x2d\x05\x88\x1a\x1b\x0f\xb0\xe1\x2a\xae\xe5\x62\x9e\xc1\xd9\x80\x4d\x28\x80\x8b\x45\x77\x24\x20\xd6\x2e\x6f\x76\x40\x7a\x1f\x9c\x37\x5f\xe2\x6e\x01\x3f\x22\x2a\xe1\x9a\x70\x36\x86\x07\x8f\x2c\xe4\x86\x96\xae\xeb\x6d\xa3\xae\xde\xbc\x9f\xf6\x4c\xf9\xd6\x98\x6f\x1c\xd1\x34\x59\x61\x1c\x26\x74\xc9\x38\x4a\x81\xbe\xf2\x38\x1a\x74\xbe\xbc\xc7\x77\x2e\x6b\x97\x10\x8e\x30\xee\xaa\xea\x28\x04\xfe\xf7\x76\xd4\xca\xb9\x49\x47\xd9\x5e\x70\x67\x27\x02\x33\x42\x7a\xd5\x98\x10\xa4\x7f\x68\x7e\x80\x9b\x50\x8c\x31\xc2\x5b\x71\xa5\x31\x97\x4f\x65\x5c\xf3\xba\x69\xe3\xd0\x7d\x19\xec\xbc\x64\x0a\xcd\x3a\x7d\x63\x2d\xed\xc8\xeb\xd7\xaf\x1b\xf6\x21\x2e\xa5\x20\x55\xd3\x4a\x2d\x7f\xa0\xd9\x82\xd6\x6e\x4f\x0a\x03\x1c\xba\x1a\x01\x0e\x4c\x45\x2f\xf2\xe5\x68\x1e\x15\x3f\xa7\x59\x9d\x94\xa4\x01\x4b\x56\xba\x2f\xbf\xfa\xea\xbb\x41\xab\x02\xaa\x74\x2b\x2e\x69\xcf\x3a\xe2\x38\xd7\xdf\x5a\xf1\xd3\xc3\x69\x4a\xd1\x61\xa4\x1e\xa4\xa1\x09\x06\x4b\xd8\x48\x01\xd9\xdf\x2a\x24\x0e\x60\x6e\x49\x5b\x7c\x70\x21\xf4\x51\xc2\xc8\x43\x05\xcb\xd2\x95\x60\x5a\x06\x20\x64\xa7\xb2\x6c\xab\x51\xd3\xa0\x17\x31\x12\x9d\xe8\x8a\x01\x28\xcf\xdc\xaf\xcc\x42\xa5\x25\x50\xf3\xe6\x8a\x76\x32\x5e\xbf\x7e\xed\x02\x73\xea\x47\x55\x2a\xc2\x34\x06\xcd\x12\xe0\x5b\x58\x38\xf0\x98\x6c\x4a\xd9\x5d\xe5\xa3\x59\xd3\x11\x23\x5d\xa5\x75\x35\x4d\x47\xd5\xc2\x8d\x92\x51\x90\x63\x45\x85\xe8\x00\x30\x4a\xb1\x6e\x05\x8c\x02\xb9\xee\xf6\x56\x68\x63\x1e\x25\xa6\x75\x8b\xd3\x82\x80\xb8\x61\xfd\xb3\x20\x9f\x65\x41\x51\x39\x86\x12\x98\x46\x3b\xc3\xea\x3d\x92\xb7\xb3\x15\x1d\xf2\x83\xd4\x9f\x33\xc4\x75\xb0\x79\xb8\x58\xbd\x87\xd3\x20\x7f\x97\x45\xe3\x4a\x9c\x95\xc0\xdc\x58\x09\xbc\x7a\x2f\x45\xd8\xa1\xbc\xaa\x97\x0a\xe6\x86\x6d\x8c\xd0\x15\x5a\x45\x33\xe5\x60\x5f\x89\x86\x64\x4c\x86\x7f\x70\x5b\x9b\xaa\xbe\xd9\xa0\xa8\x45\xcc\x42\x8c\x6b\x97\xfe\x58\xdb\x31\xa0\x2b\xcd\x51\x64\xbc\x58\x08\xc6\x45\x9a\x49\xf1\x47\x5a\x3e\x80\x19\x71\x8f\x30\x58\xc3\x96\x58\x40\xfb\x1a\x9b\x48\x4b\x07\xe7\x29\x6a\x0f\x3d\xb7\xe3\x50\x07\x19\x05\x4b\x25\x78\x3c\x76\x60\x99\xd3\xa3\x38\x45\xdc\x7a\x42\xd7\xc3\x50\x21\xc3\xcd\x5b\x37\x76\x3d\x69\x8e\xd1\xa7\xc5\xac\xd3\xed\xb9\x24\xfb\x3a\x9d\x22\xd9\xb8\x59\x97\x7c\x03\xd5\x86\x18\xd5\x2e\xf4\x25\xf6\x3b\xa2\x40\x7f\x1a\xa7\xa3\x20\xee\x33\xa4\xf6\x03\x37\x59\xc4\x3c\xf3\x35\x19\x8d\x83\xc5\xdb\x9b\x36\xcb\x0a\x3b\x8d\xf2\xc4\xaa\x26\x91\x55\x8a\x6e\xd0\x7e\xfa\x80\x63\x4a\xc9\x02\x1d\xff\xf4\xd4\x79\xa3\x7a\x59\xcc\xb4\x3d\x9f\x65\x98\xd3\x1a\x6e\x3d\xed\xb5\x1c\x03\x21\x61\xa0\xae\x2d\x73\x5a\xc3\xed\x6f\x20\x81\xcf\x69\x6b\xb8\xfd\x1d\xff\x54\xb4\xd0\x1a\xee\xf0\x22\xd1\x28\x48\x5a\xc3\x9d\x9d\x9e\x69\x3e\xf8\xff\xb1\xf7\xee\xfb\x6d\xdb\xcc\xa2\xe8\xdf\xc9\x53\xa0\xdd\xe7\x6b\xa4\x98\xb6\x75\xb7\xa3\xc4\x5d\xcb\x91\xed\xd8\x2b\x71\xec\x6d\x3b\x6d\xbf\xed\x9f\xeb\x1f\x25\x42\x16\x1b\x89\xd4\x22\x29\x5f\xd2\x78\xbf\xcf\x79\x8e\xf3\x62\xe7\x87\xc1\x85\xb8\x52\x94\x2f\x69\xda\x65\x7f\x6b\xa5\x22\x09\x0c\x06\xc0\x60\x30\x18\xcc\x05\x1e\xd9\x20\xfd\xd8\x6d\xb5\xe0\x99\x9b\x11\xfd\xd8\x6d\x51\xf0\x8c\xb3\xff\xd8\x6d\x51\xb4\xf8\x65\xef\x8f\xdd\x16\x69\x90\x1b\x01\xfd\xd8\x6d\x35\x6f\xcf\xbc\xe6\xab\x27\x7b\xc4\x27\x7b\xc4\x7f\xb6\x3d\xa2\xcb\x18\xf1\xde\x36\xf3\xe5\xcd\x04\x4b\xd8\x00\x42\xb9\x8f\x38\x7b\x4c\x13\x7b\x78\x3b\xdf\x64\x25\x37\xae\xbf\x8b\xcd\x4a\x09\x93\xfa\xd5\xd5\xd5\x3c\x26\x8d\x2d\xce\x0d\x4b\xd8\x48\x58\x3c\x80\xc3\xd9\x08\xf9\xd3\x50\xc2\xfd\x91\x0e\x24\x66\x32\x7a\x4d\xe0\x51\x33\xd6\xdf\x55\xb8\xc2\x38\xd1\x75\xe3\x46\x2b\xae\x42\x0b\x08\x7c\xb2\xf8\x65\x6c\x6a\x1f\x71\x66\xd9\xd4\xd4\xcd\x4b\xde\x5d\x6e\xcf\xbc\x56\xed\x69\xb7\x78\xda\x2d\xfe\xd9\xbb\xc5\x77\x6a\xbd\xfe\x70\x86\xe6\x25\xed\xe0\x73\x53\xce\x43\x9c\xa4\x71\xe4\x8f\x9f\xec\x39\x1f\xdb\x9e\xf3\xb6\x9c\x85\x5f\x84\xaf\x72\xb3\xc1\x22\xfd\x70\x5e\xd0\x54\x11\x4f\xd9\xac\x9e\x5b\x0b\xdd\xe3\x86\x32\x9c\x90\x8d\xe0\xc8\xbf\x7a\x8f\xe7\xdd\x5c\xc8\x45\x5f\x78\xcf\x9f\x3d\xd3\x71\x33\x0a\x14\x78\xa6\x95\xbf\x89\x33\xdb\x11\x1f\x24\xc3\xbd\x67\xcf\x4a\xde\x4d\x97\xbe\x82\xc3\x83\x23\x3c\x88\x2f\x69\x70\xa8\xa2\x3b\x2b\x5e\xce\x8a\xab\xfa\xb5\x60\x40\x66\xd1\x38\x1e\x7c\x2e\x47\x29\x4a\xd9\x02\x62\x71\x95\x2b\x63\xf0\x58\x6e\xdc\x9c\xa3\xf7\xc0\x37\xdf\xf9\xdc\xcf\xbd\xfe\x5e\xe4\x9a\xd3\x76\x6d\xec\xec\x52\xf9\xf9\x29\x37\x3b\xc5\x73\xb3\xc8\x5d\xa6\x3e\x37\x1a\xf2\x36\xc9\x9a\x35\x2c\x35\x22\x2d\xde\xfc\xad\x42\x41\xd2\xed\x09\xa7\x6a\xd7\x6d\x87\xf3\x52\x44\x02\x27\xcb\xbb\x8f\x77\x3e\xd8\x9c\xa3\x16\xce\xa7\x43\x2e\xec\x10\xcb\x4d\xb9\x9c\x6f\xb7\xb9\x70\x6e\x51\x11\x69\x5a\x21\x5d\x4e\xaf\x3f\xc9\xe9\x4f\x72\xfa\x3f\x5b\x4e\x67\x42\x7a\x3a\x72\x68\x75\xe6\x88\xdf\x38\xc1\xb3\x09\x01\xfd\xf3\x1c\x25\xd0\x20\x4e\xf0\x4a\x18\xab\x72\xfa\x5a\xe9\xc0\x09\x25\x1d\x2d\xe7\xf9\x6b\x42\xa1\xe3\xd1\xe8\xd1\xb5\x43\xdf\x8f\x3c\x4e\xb8\xe3\xf1\x48\xb9\xdd\xc0\x57\x2c\xe8\xf4\xce\xb7\xb8\xd0\x49\x47\xf3\x2f\x74\xd2\x11\x5c\xe8\x50\xc1\x65\x91\x7b\x9b\x22\x39\xdf\xbd\x39\x19\xe2\x81\xb4\x35\x5d\x5a\x6f\xea\x98\x88\x90\x8e\x46\xe7\xf6\x02\xaa\x51\x08\xb2\xe8\xb2\x8a\x1a\x0d\xa3\x61\xec\x6e\xd1\xf2\xf5\x7e\xcd\xa5\x38\xdb\xf7\xaf\x19\x11\x1c\x87\x5f\xf4\xcb\x61\xa9\xed\x79\x45\x55\xab\x9f\xbb\x20\x12\x46\x87\xf1\xaf\xc5\x08\xd8\x8a\xdc\xaf\xe1\x89\x9f\x7c\x3e\x49\x66\x69\x86\x83\x43\x6c\x5c\x06\x4b\xcd\x17\x17\xbc\x1f\x12\x11\x26\x32\xdd\xa1\x1f\x16\xb4\xef\x2c\x73\x3f\x0a\xf0\x83\xe0\x30\x09\x2f\xfd\x0c\xd3\x23\xa1\xa3\xf5\xa2\x62\xf7\xeb\x3b\x4d\xfa\x35\xb7\xfb\x45\xc5\xee\x87\xc0\xc8\x4f\xe7\xb6\xee\x2c\x73\xbf\xa6\x2f\x70\x46\x37\xf4\xc2\xb1\x2f\x28\x75\xff\xe6\x4b\xcc\x7d\x51\xb1\x7b\xd3\xfd\xf1\xcd\xa4\xb0\x71\x57\x91\x7b\x53\xfd\xbc\x86\x5d\x45\xee\x3b\xe4\x44\x8e\xcb\x30\x05\xbd\x93\xc4\x93\x43\x3f\x4d\xaf\xe2\x24\x28\x1a\xff\x92\x75\xee\xbd\x0e\xe6\x8d\x89\xab\xc8\xbd\xc9\x70\x5e\xc3\xae\x22\x0f\xc1\x7a\xe6\xb5\x5d\x50\xca\xde\xbc\x78\x58\x5d\x45\xe9\xac\x0f\x37\x6f\x34\xa5\xf1\x2c\xca\x9f\x27\x61\x9a\x86\xd1\xc5\xf3\xd2\xd8\x4e\xe3\x54\xbf\xba\x92\xb0\xb4\x7c\xb5\xe8\x29\x50\xb1\xde\x11\xcd\xbf\xe5\x3a\x1e\x8d\xa4\x04\x62\x9a\xed\x85\x72\x8a\xd6\x2c\x23\x5a\x8d\xa7\x33\xf4\xd3\x19\xfa\x9f\x7d\x86\xce\xef\xba\xfa\x5f\xbe\x68\x77\x5d\x9b\x63\x7c\x8d\xde\xe2\x04\x5f\xa4\x5f\xfc\xf4\x4b\x88\xde\xf8\x63\x7c\xfd\x9f\x49\x36\x4c\x57\x46\x33\xf5\x38\xdc\x61\xd1\x4c\x8f\xf0\x10\x27\x38\x1a\xe0\x2e\x22\xed\xa7\xdd\xd5\xd5\x8b\x30\x1b\xcd\xfa\x2b\x83\x78\xb2\xca\x4f\xdd\xab\x17\xf1\xb2\xf8\xdd\x1f\xc7\xfd\xd5\xf4\xca\x4f\x26\xab\x61\x94\xe1\x24\xf2\xc7\xab\xa4\x4b\xf8\x3a\xe3\xff\x5d\xb9\x88\xff\xd7\x87\x66\xf3\x91\xaf\xc6\xf2\xfb\xae\x63\x82\xcd\x3f\xfc\x70\x0d\x3f\xfe\x16\x97\x5d\xd4\xf2\x15\x67\x57\x71\xf2\xf9\x08\x43\xa8\xda\x22\x45\xb9\x5e\xdc\xd4\x96\xf7\xbf\x7c\x39\x2f\x28\x75\x1f\xdf\xbc\x9b\x68\xb0\x1d\xf9\xfd\x31\x9e\x87\xa5\x54\xd2\x8e\xa0\xbd\xc0\x7d\x70\xbb\xf2\xa7\x25\x71\xcb\x4b\x3a\x70\xb3\x16\xb8\x07\x6e\x41\x7c\x15\xb1\x28\xc4\x45\x88\xf1\x62\x76\xac\x2c\x5f\xcb\xbb\x9b\x3a\x10\x9b\x4d\x4b\xa0\x45\x0b\xd9\x91\x32\xbe\xdd\x1b\xa5\x04\x67\x49\x88\x2f\xe7\x45\x80\xe0\xc5\xec\x68\x59\xbe\xde\x87\xb4\x32\xb2\xdb\xcd\x21\x2a\x52\xc6\x41\x4e\xda\xa7\x7b\x0f\xd1\x05\x2e\xe1\xce\x6c\xc7\x45\xfd\x70\x8f\x31\xa1\xd9\x1b\xe6\xc4\x48\xb5\xe3\xa0\x7e\xb8\xf7\x68\xb0\x84\x2d\xc5\xc8\xd0\x42\x76\x7c\x8c\x6f\x1c\xa5\x56\x29\x94\x0a\x6e\x75\x0d\x15\xa7\xce\x96\xa5\xdb\xbf\x9c\x1f\x4a\x2f\x73\x46\x94\xbf\xe4\x7c\x40\xba\x71\x9c\xaa\xcf\x9c\xfa\x25\x40\x84\x04\xf3\xc7\x0b\x2c\x5d\x4c\x4e\x67\xd2\x83\x24\x8b\x3f\xea\x35\xe3\x28\xbc\x74\xfa\xc6\x90\x39\x81\xef\xce\x33\x64\x31\x6c\x8b\x52\x56\x81\x0d\xdf\x1d\xc7\x2b\xcb\xf9\x8a\x08\x4b\xb6\x60\xb7\xd6\x7b\xc9\xe6\xd3\x99\xea\xe9\x4c\xf5\xcf\x3e\x53\xb1\x03\x15\xbf\x20\xfa\xb6\x51\xda\xef\x62\x58\xcd\xbd\xa3\xfc\x69\xc8\x85\x71\x9a\xe2\x2f\x1b\x15\x59\xa0\xd1\xeb\xb2\xc2\xa8\x94\xbc\x74\x76\x33\x25\xf2\x01\x8b\x40\xf9\xfa\xb9\xc4\xc0\xc3\x6c\x30\xaa\x90\xef\x7a\x6e\x91\x81\x9f\x62\xf4\x82\x50\x7c\x9a\xbd\xe8\x2a\x9f\x60\xb2\x92\x8b\x74\x25\x1d\x85\xc3\xac\xa2\x25\x14\x41\x46\x72\xc0\x9a\x59\x80\xb1\x64\x70\x5f\x8b\xf0\x15\x8d\x40\x45\x2f\x64\x5f\x5b\xd0\x98\xe2\x28\x08\xa3\x8b\x47\xc7\xe3\x90\xb6\x23\xdb\x10\xd9\x90\x62\xa1\x03\x4d\x6c\x34\x70\x46\x65\x9a\x60\xe5\x56\x92\x0e\x44\xa9\xf9\x96\x84\x0c\x9a\x2e\x23\x28\xa4\x60\x91\x9d\x2c\x52\x75\x14\x46\x69\xe6\x8f\xc7\xa5\x5a\xd6\x4a\xdb\x7d\xdd\xdd\x85\x0a\xf0\xb8\xc0\xd9\x87\xf8\xa2\x44\x40\x31\x52\xca\xe9\x63\x4f\x5b\xd4\x8a\x14\xb4\x3a\x8d\xe7\xc6\xe1\x20\x45\xe6\xb4\xd7\x1b\xf9\xd1\x05\x2e\xd1\xa4\x4d\xf8\xa0\x20\x64\x93\x2c\x65\xf4\x14\x41\x88\x74\x4c\x6a\x24\x1e\x8f\x65\x79\x60\x61\x7e\x93\x8e\x46\x2b\xc0\x1a\x0d\x76\x93\x8e\x4c\x76\xe3\x16\x9f\xe6\xdc\xd2\x18\x64\x80\x8c\x5b\x1a\xc5\x92\xe0\x41\xd5\xf4\x6e\x62\x44\x36\x4d\xfd\xe3\x21\x62\x92\x2e\x32\xae\x29\x68\xb3\x0c\x07\xbd\xe8\xfd\x9a\xd7\xc8\xf8\x01\xda\x96\x49\xcf\x90\x44\x29\x0e\x38\x1b\x75\xc9\x3f\x14\x58\x3a\x1a\x75\xc9\x3f\x54\x78\xb5\x25\x64\x68\xb5\x9e\x44\xd2\x27\x91\xf4\x1f\x2e\x92\xe6\x7a\x7e\xee\x63\xfd\x40\xd9\xa2\xa9\x7f\xf8\x11\xbe\x20\xf3\xec\x27\x9b\xfd\xd0\x91\x97\x20\x5d\x7d\xa7\x16\x85\x44\xff\x5c\x3d\x1f\x0e\xfc\xa9\x0c\xc4\x05\x63\xaf\xb7\x79\x68\x42\x90\x30\x61\x8e\xe8\xcc\x7a\x19\x6d\xa0\x17\xb5\xeb\x41\x27\x78\x15\x34\x06\x41\xab\xf5\xca\x5f\x6b\xb7\x06\xad\x57\xad\x46\xa7\x85\xeb\xeb\xb5\x57\x83\x76\x0d\x37\x5b\x41\xa7\xd5\xee\x34\xfa\x2f\x72\x5c\x6c\x60\xfc\xba\x5f\xaf\xd7\xfb\x83\xda\x5a\x6b\xf0\x6a\x30\xf4\xd7\xd6\xeb\xc3\xda\xa0\xb9\x8e\x3b\xcd\x7e\xd0\xae\x0f\x5e\xd5\xfb\xeb\xfe\xb0\x56\x7b\xe1\xe6\x4d\x14\xc7\xae\x24\xe9\xfa\xfd\xb0\x6b\x19\xc4\x9c\x13\x32\x37\xf8\xae\xb5\x7f\x74\xa7\xa7\x85\x09\xda\x06\x64\x7d\x5c\x2d\x70\xcd\xee\x52\xa8\x0a\xc7\x2c\x9e\xc5\x1f\xbb\x75\xef\xc7\x39\xf3\xf4\x63\xb7\x41\x98\x6d\xfb\x89\xd9\x3e\x31\xdb\x7f\x36\xb3\xcd\x79\x2d\x57\x7e\x69\xcc\xb6\xc8\x30\x79\x98\xc4\x5f\xf0\xc4\x8f\x56\x02\xfc\xf3\xb7\x4a\xe7\xaf\x5f\x90\xde\x27\x5d\x3f\x55\x24\x4a\xdf\xe9\x0b\x25\x23\x81\x56\x22\xd5\x4b\xdc\x25\xf7\xfe\xe2\x19\xfe\x0b\xb2\xf5\xf3\xb1\x78\xfc\x7c\xfd\x65\x73\x7c\xdf\x3b\xc5\xb7\xa5\x4b\x05\x49\xbe\x6d\xa1\x5b\xb4\x11\xfe\x0f\xdb\x5b\x5a\x17\xd2\x6d\x7f\x27\xe9\xb5\x9d\xfd\x7e\xa0\x04\xdb\x3f\x6c\x50\xc2\xd1\x5e\x91\x0d\x65\x18\x46\x38\xb8\x4f\x06\x6e\x9e\x39\x36\x8b\x11\x4b\x5b\x9d\xa7\xb3\x86\x74\xdc\xe2\xb0\x2d\x12\xb2\xae\xa0\x7d\xb2\xb1\x85\x38\x65\x94\x04\xc3\xa4\x8d\xa5\x96\x03\x7b\x6e\x36\x7d\x3e\xae\x3b\x6c\xa4\xbe\x7e\x9c\x8d\xc7\xb7\x92\xb1\x7b\x38\x44\xf8\x3a\x4c\xa1\xb8\x75\xc8\xb5\x16\x0b\x73\xca\xf3\x0c\x36\xbc\x35\x9a\xc3\x46\xce\xbb\xbf\x8c\xea\x67\x55\x47\x9a\xf9\x95\x69\x3c\xad\x54\x21\x6f\x35\xbb\xf7\x22\xfc\x1f\xd6\x13\x8c\xd6\x0f\xb2\x70\xa3\x0e\x37\xb5\x6f\xc8\x31\xcb\x62\x3b\x29\xaa\x76\x10\x2e\x62\x64\xaf\x78\x2f\x9c\xd4\x58\x3e\x7d\x37\xd4\x11\xa2\x24\xe2\x09\x4a\xf2\x74\xde\xef\x70\x56\x91\x4e\xe7\x38\x9a\x4d\x70\xe2\xf7\xc7\xb8\x8b\xb2\x64\x86\x4d\xd5\x9f\x3f\xc1\x69\x61\xaa\x6e\x29\x9f\x37\x14\x06\xe5\x2d\x92\x72\x78\xa7\x73\x92\x78\xa7\x5a\x16\xef\xd4\x91\xc6\x5b\x2f\xf2\x5a\xd1\x88\x89\xe6\xeb\x3c\x7b\x3f\xed\x84\x3d\xbb\x4b\xdc\xff\xc3\x83\xf2\x1e\x1d\x32\xd6\x17\x02\xdf\x4f\x6f\xa2\xc1\x3b\xd8\x6f\x88\xc8\x0b\x5d\xa8\x9e\x29\x39\xd1\x37\x59\x91\x8a\xe4\xa6\xa1\x55\x53\x26\x09\x40\xa8\x2c\x03\x6e\x97\xd1\x12\xe0\xb0\x32\x18\xf9\xc9\x66\x56\xa9\x55\x57\xb2\xf8\xd3\x74\x8a\x93\x9e\x9f\xe2\x4a\x95\x7f\x86\xac\xca\x95\x7a\xd5\xb9\xf1\xf0\x99\x75\x67\x1e\xcd\x37\xee\x3c\x8d\x2a\x8f\x88\xc6\x6b\x5c\x90\x0e\x99\x2b\x46\x08\x28\x4a\x82\x6d\xf1\xd6\x96\x62\x5b\x55\xf4\xf0\xcc\xf6\xa2\x0a\xdd\xee\x25\x8d\x4d\x9e\x8a\xbb\xa8\x83\x7c\xd4\x17\xeb\x65\x7e\xd7\xef\x0e\x02\x86\x72\x33\x27\x6b\x87\x68\xda\xf3\x05\x7b\x55\x32\x3d\xba\x9a\x12\xdd\x3e\xd8\x66\x52\xf4\x5b\x35\x7b\xf9\x05\xce\x16\x4c\x5e\x7e\x81\x5d\xdb\xc9\xf7\x9d\xbb\xdc\x42\x1c\xe5\xb3\x97\xeb\x66\x73\x5d\x59\x1e\x35\x95\xe4\xa7\x67\xaa\x7a\x9d\x4c\x13\xab\xa2\x6d\x56\x25\x13\xa1\xcb\x53\xf6\x58\xe9\xd0\xf9\x00\x49\x07\x73\x2d\x66\x0f\x39\x62\x77\x9e\x8e\xd8\x4f\x47\xec\x7f\xf6\x11\x5b\xd2\x67\x32\x0e\x31\x61\x2c\x5d\x3d\x69\xff\x17\x1e\x0e\x13\x7c\x83\x7e\x0d\xc7\x83\xcf\x18\xbd\xf9\x03\x0f\x87\xae\x68\x3d\x0b\x85\xf6\xd9\xf7\x13\x72\x84\x3f\xf0\xa3\x01\xf6\xa1\xac\x2d\xa8\xcf\x1d\xe2\x00\xb1\x2a\xef\xfc\x4b\xf4\x6b\x1c\x07\xe8\xcd\x85\xf3\x90\xdf\xca\x0f\xf9\xff\xc5\xb8\xa9\xe2\x3c\xcc\x58\x6c\x51\x4a\x5b\x4b\xa0\x3a\x3d\x0b\xad\x2d\x05\x2d\x4e\x92\x58\x0b\x1e\xb4\x4a\xdf\x51\x1b\x04\xba\xed\xec\x65\x2f\x52\xb2\x31\x4e\xe3\x28\x0d\xfb\x63\x4a\x60\x53\x1f\x9c\x48\xd0\x84\xdd\xf9\x90\xbd\x68\x9a\xc4\x97\x61\x80\x93\x54\xd4\xf2\xc7\x69\x6c\x56\x8d\xc7\x63\x52\x95\x50\x1b\xb7\x1e\x47\x51\x1c\xd0\xaf\x61\x34\x88\x27\x32\x64\x02\x8c\xe5\x14\xa0\x57\xae\x59\x38\xc1\x64\xb1\x85\x29\xaa\xa3\x14\x0f\xe2\x28\x80\xdd\x31\x8c\x2e\xc6\x38\x8b\x23\x18\x4e\xd2\xbd\x82\x83\x3e\x47\x55\x39\xee\xf3\x97\x68\x43\x74\x45\xd2\x33\x90\xb6\x41\x03\x7c\x2b\xbd\xe4\xb8\xc8\x5a\x07\xe7\xe1\x8f\x48\x28\xa3\x24\x8e\xe2\x59\x3a\xbe\x81\x30\x18\x8e\x7d\x98\x7c\xb2\x9c\x47\x50\xe0\x67\xbe\xf3\x84\xac\xf6\x56\x51\x79\x44\x81\xd2\x79\x02\x46\x3e\xa9\xfd\xa0\xf4\x5e\x49\x6e\x18\x47\x69\x4c\xb6\x2e\x42\x14\x15\x4a\x1a\x2b\x7b\xd1\xa5\x3f\x0e\x83\x43\x56\xbe\x22\xcb\x3c\xdc\x0b\x1b\x06\x43\x92\xf0\xd5\x3d\x9e\x91\xf9\x4a\x16\x1f\xd2\x77\x80\xd2\x0a\xed\xbd\x07\xdd\x64\xc6\x16\xd2\xf9\x85\x9d\xca\x37\xd4\xb9\xa2\xc2\x2c\x03\xcd\xaf\xca\xa1\x53\xbc\x91\x30\xfd\x85\xa0\x7b\x44\xa9\x10\x0b\x41\x4d\xea\x66\x36\x4a\xe2\x2b\xa4\x76\x4f\x2f\xaf\x74\x87\x75\x93\x7e\x5a\x29\x75\xf2\xf7\x17\x9a\x7d\x90\x66\x0b\x49\x40\x3f\x97\x0a\xe9\x67\x3e\x31\x00\x70\x83\x22\xa4\xe0\xb9\xa5\x68\x83\xa7\xce\x94\x64\xe3\x22\xea\x78\x18\x42\x30\xe7\x9e\xca\xfd\x0c\x64\x09\x79\x9e\x74\x0a\x27\x89\x2e\xe2\x5b\x7a\x53\xd5\xcd\x6d\xc8\x9f\x02\x67\x11\x1a\x9b\x3f\x64\x46\x6d\xb9\x7d\x43\xc8\x65\xd9\x5e\x15\x12\xd4\x83\x73\xba\x8f\x0d\x36\x6a\x2c\x3a\x19\x90\x02\x6f\xc9\x77\x8b\x92\x89\xd6\x7b\x08\xc2\x84\x16\xbe\x33\xc2\x04\x9c\x64\xea\xe4\x4c\xe6\x6e\xa4\x98\x3e\x00\x2d\xaa\x34\xc8\xf5\x6c\x30\x1b\x15\xde\xca\xbd\x48\x2f\x9d\x47\x7b\x4a\x87\x04\xd1\xa1\x39\xdb\x1f\xce\xc4\xbe\x4a\xa4\x4d\x7e\x26\x64\x22\x9f\x41\x71\x19\x9f\x2a\xbb\x6a\xae\x90\x96\x44\x5d\x75\xd7\x77\x6e\xf7\xf3\x76\xee\x8c\x1c\xa9\x98\xe0\xa2\x23\x4a\xbe\x1d\x8a\x4f\x73\x39\x36\x8d\xfd\x7f\x0b\xd0\xf6\x82\xb9\x4b\xc6\xf2\x55\x98\x25\x71\x4c\xb2\x38\x88\xd1\x60\x8c\xfd\x68\x36\x45\x11\xc0\x27\x03\x2c\x8e\xed\x45\x43\x25\x61\x6f\x59\x79\x14\x49\x39\x20\x8a\x68\x5c\x1d\x4b\x22\x1c\x9d\xd2\xd2\x67\x44\x48\x22\xd5\xbb\x88\x02\x09\x83\xae\x01\xa8\x6b\x03\xd9\xcd\x7f\xde\xf2\x9c\xd8\xab\xab\xfa\xe8\x2b\x0c\x80\x09\x60\xea\x6e\xce\x10\xaa\x88\x15\x3e\x67\x72\xe3\xa9\x10\x4a\x89\x08\xca\xcc\x68\xe1\x74\x73\x11\x92\x23\x5d\xa8\xeb\x8e\x49\x1d\xcb\x9c\x1b\x73\x5b\x38\xf2\x02\x84\x4a\xa4\x50\x97\x77\x88\x5a\x96\x59\x06\xf9\xb5\x34\x3c\x39\xfe\x6c\x74\x2a\x4c\xa3\xfa\x19\xdf\xa4\x95\xbc\x6e\x95\x6b\x79\x21\x59\x3c\xfa\xe9\x27\xe4\x1a\x43\x42\x4c\xc9\x09\x7d\x5f\x51\x0a\xbd\x56\xc7\x59\x17\x80\x0b\xc6\x3b\xdf\x7d\x12\x4c\x78\x01\x91\xff\xf9\xb0\x4f\xf0\x60\xe4\x47\x61\x3a\xe1\xc7\xd0\x62\xe6\x00\x00\x8a\x87\x97\xb6\x21\x0f\xec\x67\x8c\xa7\x22\x7f\x00\xef\xec\xea\xcb\x3f\xd2\x51\x18\x91\x86\xae\x07\xf1\x64\x3a\xc6\xd7\x61\x76\xd3\x6d\xc3\x91\x8c\x14\x20\x04\x51\x21\x9b\xc3\x67\x7c\x43\x35\x05\x62\x34\xa5\xf1\x5a\x5d\x45\x09\x9e\xc4\x97\x18\xf9\xe3\x31\xf4\x2a\xf5\x10\xbe\x1e\xe0\x69\x06\x62\x3f\x7b\x25\x97\xcf\x46\xf8\x06\x45\x98\x8e\x48\x1f\xb3\xfa\x01\xe9\xf1\xcc\x1f\x8f\x6f\x50\xff\x06\x86\x8c\x0c\x0f\x4b\x05\x00\x34\xf3\x2b\xd9\x90\xc2\xe8\xa2\x52\x95\xf6\x81\xca\x0f\x4a\xef\xd0\xd7\xaf\x04\xdf\x95\x30\x0a\xf0\xf5\xc1\xb0\x02\x6e\x8a\x84\xd8\xce\x5f\x54\x61\xf2\x97\xeb\xfa\x06\x21\x51\xd8\x67\x7c\x73\xb6\x22\x56\xa2\x6e\x0e\x6d\x52\x24\x29\x6f\x98\x26\xff\x8d\xc9\x13\x4e\x99\x64\xde\x07\xd4\x36\x17\xc5\x51\x19\x9e\x40\x4d\x6a\x8b\x68\x92\x59\x0c\x9b\x2a\x50\x07\x15\xa2\x0e\x01\x67\xe9\x4c\x8a\x33\xa5\xf7\x04\xb0\xa4\x8a\xf4\xd0\x60\x65\xfb\x64\xf7\xfc\xf0\xe0\xc3\x87\xbd\x8f\xef\xce\x4f\xf6\xf6\xb7\x0f\x3e\x9d\xc8\xc7\xa3\x32\x33\x60\x0a\x55\x8a\xc4\xf4\x28\x47\x47\x53\x26\x23\x78\x6d\xf9\x99\x8f\x36\xd0\xe9\xd9\x6b\xf5\xfd\x1e\xb8\x1b\xf3\xd7\xe5\x96\xaa\x00\xb8\x32\x9d\xa5\xa3\x8a\x4e\xf7\x4c\xc4\x53\x4a\xef\x05\x29\x2d\xfc\x19\xdf\x54\x8d\x31\xc8\x01\x2e\x30\x78\xa5\xc4\x4d\x01\x99\x35\xca\x97\xd4\xc4\x9f\x2a\x4c\x32\x04\xb2\x05\x86\x02\x24\x46\x48\x53\x1d\xa6\x7d\x7f\x2a\xa9\x2e\x24\xbd\xb6\xea\x29\x4e\x05\x57\xe0\x1a\xd5\x3f\xf5\x31\xd8\xf7\xa7\xa7\x50\x2d\x84\x2d\x9e\x8f\xcc\x29\x14\x3f\x93\x3c\xd2\x45\xe3\x8a\xdf\x3c\x5a\x58\x66\x8e\x55\xa9\x59\x09\x6f\x72\x72\xb0\x75\xd0\xe5\x44\x86\xc6\xf1\xc5\x7f\xe8\x52\x75\xec\x90\xab\xef\x2b\x49\x97\x50\x16\xa4\xd6\xa3\x23\xfb\xb6\x32\xf1\xa7\x15\x97\xb1\x02\xff\x03\xfb\xc5\x20\x1f\x65\x32\xf6\xec\xa8\x17\x06\xb2\xe3\x8d\xa0\x88\xcf\x18\xa5\xb3\x04\xf4\xc4\x9c\x59\x85\x29\x4a\xb3\x90\xd0\x03\xe5\xe4\x38\x40\xfe\x10\x1c\x84\x92\x24\xbc\xf4\xc7\xda\x5e\xab\xc0\x24\x03\x02\x6e\xff\x74\x69\x84\xc1\x99\x8e\x62\xde\xa5\x95\x41\x6e\x0f\xa0\xd6\x11\x5f\x9c\x0e\x33\x5c\x77\x22\x7f\xba\x45\x78\xcc\xf4\xcc\x96\x1a\x43\x7f\x9c\x62\xf9\x96\x8d\xb9\x3d\xcd\x1d\x53\x91\xce\x9f\xb5\x89\xee\x00\x83\xcc\x0b\xcc\xb8\xb4\x68\x1d\x87\xff\xd7\xc6\x78\xfe\x00\x35\x4b\x8c\x63\x79\xc5\x00\x52\x28\x4c\xea\x25\x54\x54\x47\x49\x5b\xec\xee\x61\x52\x71\x71\xeb\x19\x90\x7c\xc9\xe9\xca\xb8\x74\xa4\x07\xd5\x50\x6f\xbc\xb4\xd4\x4b\x66\xea\x0a\xa6\x90\xfe\xb1\xdb\x80\xd0\x3e\x4c\x19\xfe\x63\xb7\x09\x6e\xa8\x6b\x65\xee\xc8\x58\xcc\x4d\x9c\x65\x61\x74\x61\xf7\xec\x05\xc6\x14\x48\x99\x6b\xd1\x86\xf0\x59\x7b\x6d\x94\xc8\x23\x3d\x0b\xfb\x20\x57\xd0\x22\xd6\x28\xeb\x37\x41\x79\xfd\xe9\x5a\xef\xe9\x5a\xef\x1f\x7e\xad\xc7\x22\xfa\xb2\x53\xcb\x5d\xa2\xfa\xce\x33\x87\x75\xe4\xbe\xd0\x52\x5f\x2c\x62\x38\xcb\x97\x74\x9d\x1d\x0e\x36\x83\x20\x85\xa1\x13\xbb\x9b\x1f\x81\x5a\x2a\x45\x33\x2a\x7e\x31\xa7\x37\x8f\x08\x5f\x61\x06\x91\xf2\x10\x24\x05\xa0\x9b\x2a\xdd\xed\x9f\x3f\x97\xcf\x07\xec\x7c\xf6\x5c\x57\x12\x91\x6d\xf3\x39\xbb\xb6\x92\xca\x49\xbc\x8a\xc6\xe9\xe1\xae\x74\xa4\x5c\x1c\x31\x87\x2b\x85\xa3\x31\xb9\x89\x8c\xbd\x45\xd5\xe8\x12\x8a\xe8\xbe\xcd\x7b\x9a\x5a\x36\x0b\x9b\x3d\x0e\xff\x53\xf7\x2d\x7d\x7b\x72\xe9\x2e\x85\x85\x20\x0f\x44\x04\x28\xff\xf4\x13\xe0\x4e\x15\x53\x61\x74\x01\xdc\xb8\xaa\x40\xe4\xd7\x17\xf3\x52\x9a\x52\x88\xb2\x97\xf2\x5d\x3b\x29\xa4\xa1\xb1\x9f\x42\x33\xc7\x19\x99\xec\x1f\x36\x36\x8c\x81\xe6\x7f\xc6\x8b\xd5\x55\x9a\xb9\x5d\x21\x29\x58\x6a\x59\x32\x23\x32\x5b\x92\x66\x28\x8d\xa9\x9d\xe3\x74\x0a\xac\x1b\xce\xce\x7e\x74\x93\x91\x03\xbf\x87\xfa\x78\x48\x18\x00\x5d\xe2\xfc\x0a\x15\x46\x83\x2a\x19\xb5\xbf\x70\x58\xf9\xc1\x82\xf5\x4f\x3f\x21\xdb\xc8\x57\x8d\xfa\xc8\xbc\x6e\x20\xa8\x5a\xdc\xa3\x9d\x9d\x8d\x29\xdf\x8c\xf0\x75\x86\x7a\x87\x9f\xd0\xe0\x66\x30\xc6\x9e\xe8\x26\x0c\xbb\xd8\x6c\xa0\x27\xd0\x65\x66\xb3\x34\x4d\xe2\x01\xe1\x59\x29\x1d\x1d\xa3\x15\xe9\x18\x2c\x96\x89\x6d\x2e\x2c\x1d\x61\xa4\xa1\x97\xba\xf5\x50\xad\x4c\xff\x2c\xc3\x4a\x49\xc1\x25\x9a\x49\xc6\x60\xcf\x05\x00\xdd\x8c\x4d\xd2\xc5\x56\x4c\x3b\x28\x47\xba\x5f\xdd\x12\xea\xd6\xcb\x85\xf0\xbd\xc0\xcb\xd9\x04\x7b\x2f\xeb\x90\xa8\xce\x00\x38\x0b\x59\x27\xdc\x4e\x72\xcf\x9a\x96\xd3\x99\x6b\xb3\xd8\x64\x5e\x93\xff\x90\xac\x6b\xda\x23\x72\xb4\xa4\x9c\x5a\xa2\x5c\x78\x69\x49\x2a\x27\xd6\xab\x74\xd2\x87\x0f\x7e\x10\x08\xdb\x2e\x29\xef\xa7\xf8\xae\x4f\x8f\x74\x70\x90\x58\x2c\x37\xde\x82\xf7\x92\xad\x38\x15\xe8\xc4\x48\xc8\x96\xbe\x79\xbb\x85\x16\x8b\xe1\x30\x7f\xa5\x6a\xa5\x72\x16\x04\x5a\x05\x0d\xf9\x52\x48\xc8\xb3\xe8\x96\x68\x0d\x02\x13\x2a\xe7\x8a\x34\x07\xd5\x82\xd1\xb6\x4a\xb5\x02\x21\xb7\x01\x1b\x91\xd5\xd5\x6c\x17\x44\xf6\x7d\xca\x51\xfa\x24\xfb\xfe\xd3\x65\xdf\xdc\xa4\x8d\x27\xec\x7d\x28\x1f\xdd\xbd\xbe\x1f\xa9\xd2\x6e\xd8\xf7\x85\xeb\x2d\xbe\xa6\xea\xea\x22\xd7\xdd\xe3\x89\x9f\x64\xdb\xac\x60\xee\x76\xeb\xbc\x1a\x03\xb5\x12\x34\xcb\xfb\xa2\xe9\xbc\xa5\xd7\xe2\x12\xec\x38\x4b\xc2\xe8\xe2\x16\x5c\x5b\x6c\xef\x89\xb4\xdc\xf7\x23\xf9\xd3\x2f\xfe\x78\x86\x6f\xd1\x25\xf9\x0f\xbb\x0e\x21\x90\x87\x38\xc1\x73\x6e\x48\x3d\xd5\xbc\x00\x82\xd4\x30\x9c\x54\xb1\x38\x1b\x79\x80\x11\x91\xd6\x3d\xda\x92\xb9\x85\x81\xda\x8d\x8e\x32\x64\x9b\xee\xfb\x51\x25\x8b\xab\x4c\x55\x04\x3a\x1c\xf2\x99\xab\x7c\x2a\x16\x2b\x22\x52\x0f\xd2\x44\x54\x5e\x84\x54\x7d\x43\x21\x32\x3f\xdd\x17\xa6\xfe\x98\x41\xdc\x0a\x13\x22\x8b\xd9\x1c\x62\x78\x8f\x4e\x62\xe6\xd9\x2b\x77\x07\xaa\x33\xe8\x95\xaa\xd9\x35\xde\x9e\x90\x63\xa0\x1b\x36\x49\x17\x5c\x24\x84\xa7\x34\xce\x46\x72\x4a\xf0\x4a\x15\x1a\x61\xd8\x46\x69\x16\x66\x33\x2a\x70\x99\xe6\x5f\x01\x9e\xc6\x69\x98\xc9\x58\x32\xb8\x02\x3d\x00\x33\x18\x87\x38\xca\x74\x4b\x8c\xd2\x0d\x1b\x26\x16\x3c\xd5\xb8\x39\x82\x8b\x62\x64\x8e\x1f\x57\xc1\x17\x5e\x25\x0b\xd2\x1b\xce\xa2\x00\x6c\x22\x07\x38\xc9\xfc\x50\x4c\xbf\x63\xf9\x88\x89\x5d\x6c\x1d\x3d\xfa\x12\x12\x78\xdd\x61\x2d\xb1\x91\x27\xb3\xa9\x65\xfc\x92\x64\x5b\xe1\xbd\x9e\xc5\xb9\x44\x4b\x40\x77\x69\x03\x12\x6d\x8e\x67\xb8\x4b\xff\xc3\xc5\x5c\x2d\xd9\xbb\x73\x56\xd8\xe4\xe7\x93\x02\x71\xed\xc3\x01\xe2\x9c\x10\x71\x0e\x89\x2a\x93\x59\x9a\xc1\x56\x87\x27\x38\xca\x04\xdd\xf4\x6f\x32\x9c\x36\x1b\x55\x26\x8c\xff\x50\xd5\x26\x92\x95\x7b\xf0\xe9\x23\x93\xa0\x4d\x20\xaf\x4f\x49\x15\xcd\xa2\xf0\xbf\x67\x18\x85\x01\x8e\xb2\x70\x18\xaa\xac\xb8\xd4\x64\xf3\xe1\x29\x31\xc5\xd0\xa4\x9d\x6d\xfa\xb0\xed\x48\x9b\xd0\x6b\x9d\x0a\xf8\x20\x57\xfc\x7e\x58\x5d\xf1\x33\xc2\x59\x57\xf8\x00\x73\xd0\x7f\xde\x97\x0a\x0c\x61\x95\x0f\xa3\x35\x0a\x82\xb9\x21\xfe\xd8\x6d\x12\xd9\x95\x67\xee\xbf\x3d\xf3\xda\xa5\x72\x25\x33\xf5\x6e\xbb\x54\xc2\xb6\xd7\xb2\x16\x3e\x26\x02\xc6\xd0\x1f\x64\x71\x72\xe3\x51\x8d\x32\x19\xd8\x67\x84\x4f\x13\x59\x3f\x1e\x22\xd1\x9b\x8d\x0d\xf4\x23\x8d\xc8\xf4\x23\x94\x79\xb6\xba\x8a\x7a\xf1\x64\x12\x47\xff\x75\xfc\xfc\xd9\x33\xa3\xf3\xf9\x2f\xd6\x00\xc7\xa9\xf2\x23\x19\x86\x04\xff\x58\xf5\x90\xf4\x0a\x47\x83\xe5\xbe\x9f\xe2\x4e\x4b\xfb\x30\x09\xda\x7a\xd1\xcb\xe9\xe7\x60\xa8\xbd\x1c\x84\xd3\x11\x4e\x96\x29\xe4\xea\xeb\xe7\xcf\x6e\x9f\x3f\xc3\xe3\x14\x23\xa9\x33\x54\x63\x4e\xfb\xc2\x87\xe1\x47\xf4\xd3\x4f\xec\xc3\x8a\x3f\x09\x44\xdf\x36\xf7\xb7\x9e\x3f\x7b\x46\x3f\x54\x4e\x39\xce\x1e\x52\x51\x85\x67\x82\x21\xfd\x40\x11\x83\xdf\x32\x3e\x67\x62\x94\x65\xc4\x58\x43\x34\x1c\x06\xaa\xf4\x93\xf8\x2a\xc5\x49\xf5\xf9\xb3\x67\x62\xc4\xe2\x38\x5b\xe9\x25\x37\xd3\x2c\xfe\xaf\x63\x5a\xf5\x16\x8e\x4f\xf2\xfe\x23\xbe\xa3\x3f\x9f\x3f\x7f\x56\x51\xcf\x63\xcf\x10\x55\x89\x1c\x8f\xe2\x24\x1b\xcc\xb2\x94\xbe\x21\xcb\xa6\x87\x36\x10\xaf\xfb\x5a\x7a\x7d\x3e\x0e\xfb\xe4\xd3\xca\x38\xec\x4b\xef\x41\x1b\xd6\x83\x4e\x91\xaf\xa4\xd4\x8a\xf4\x4e\x81\xe0\x8f\x2f\x62\x00\x41\x7e\xbc\x7e\x2e\xb0\xf8\x10\xc7\x9f\x67\x53\x94\xf9\xfd\x31\x96\x30\x39\x7e\x7b\xf0\x1b\x3b\xf4\x89\x77\x7b\x1f\x7f\x39\xb7\xbd\x3f\xfe\xf4\xf6\x7c\x7f\xef\xb7\xf3\x9a\xeb\x43\xdd\xf5\xa1\xe1\xfa\xd0\xb4\xb6\xed\x6a\x47\xfe\x68\xb4\x25\x7f\x34\xda\x93\x3f\xf2\x36\xc5\xd0\xf4\xe2\xc9\x94\x9c\x14\xc7\xe6\x10\xd9\xa6\x54\xab\x15\xc4\xb3\x3e\x11\xfb\x49\xad\xbc\x00\xb0\x58\x19\x0b\x24\x9b\x2a\x84\x10\x4e\x10\x85\xe8\x0d\x6a\xb4\x3b\xaf\x51\xb8\xb4\xa4\x80\x17\x42\x22\x7a\x83\xea\x8d\x75\xe3\x1b\xf9\x0b\x4e\xc3\x33\xb4\x41\x60\xbc\x41\xf5\xd7\xea\x77\x7a\x97\x5a\x50\xab\x42\xab\x55\xd1\xef\xa8\x76\x5d\xaf\xf7\xf5\xfa\xf9\xe3\xed\x73\xa5\xd7\xbf\xfa\xe3\xcf\xe8\xdd\x4e\xa5\xf1\xfb\x7a\x55\xed\xed\x35\x0d\x91\xa8\xbe\x0b\xb5\x97\x0b\x8d\x80\x34\xc8\x69\x3f\xbe\x56\x3f\x82\xa5\x01\x69\xf3\x3a\x44\xbf\xa3\xca\x75\xde\x21\xf6\xbb\x21\xfd\x6e\x4a\xbf\x5b\x55\xad\xb3\x00\xa5\x92\x5e\xa3\x9f\x7f\xfe\x19\xad\x43\xc9\xf4\x1a\xfd\x84\x6a\xd7\xc3\x21\x1d\xa0\x4e\x53\xab\x42\x56\xc7\xe9\x35\x19\xc8\xf4\x5a\xfb\xc4\x17\xcf\x69\x0a\xdf\xaf\x5f\x3f\x77\x76\x6a\x32\x1b\x67\xe1\x74\x1c\x0e\x40\x4d\x60\x76\xef\x9a\x90\x71\x70\x7a\x7d\xf6\xda\xf2\xad\x45\xbf\x35\xac\x1f\xd7\xe9\xc7\xd6\x59\x41\xeb\xe9\xac\x8f\x40\xc0\xf1\xd0\x24\xbc\x46\x83\x78\x3c\x9b\x44\xa9\x42\xfd\x32\x4c\x22\x29\x54\x02\xe8\xd5\x4b\x42\x33\xb5\x3a\x1f\x29\xf6\x58\xab\xd7\x6a\xfa\xd0\x8a\x95\x4c\x07\xab\x92\xc1\xc4\xb4\xaa\xe8\x2b\xf9\x4d\xc7\xdb\x51\xa5\x2e\x57\xa9\x77\xa4\x2a\xf5\x8e\xab\x4e\x43\xae\xb3\x5e\x45\x79\x9d\x86\x31\xeb\x82\x1b\xd0\x3a\x59\xc1\x48\x85\xd1\xa5\x3c\x5a\xe4\xb1\xf4\x88\x5d\xaf\x4b\xe3\xc3\xc8\xb3\xc5\x5e\xd5\xf8\x8b\x86\x32\xa4\x85\x23\xaa\xf0\x47\x46\x63\x65\x86\x55\x61\x9d\x4a\xbd\x39\x63\xab\xb0\x55\xa5\xe2\x9c\x01\x56\x58\x2e\xab\x58\x34\xca\x70\x5b\x00\x8a\x60\x9c\x98\x9c\xf0\x87\x6b\x2b\x13\x64\x0c\x60\x63\x01\x0e\x08\x55\x1a\xe8\x77\x14\x9c\x92\xff\x5d\xaf\xa3\xdf\xd1\x75\xe3\xec\x4c\x5f\x48\x50\x36\x44\xbf\x6f\x40\xc1\xeb\xd0\x28\xa0\x30\x49\xf8\x79\x0b\x87\x5a\xb1\xaf\x1c\x26\x78\x40\x3b\x17\xa0\xa3\x41\x1c\xb1\x0d\x26\xdf\x95\x8e\x7a\x07\x1f\xc9\x1e\x51\xbb\xae\xd5\x3c\x54\xbb\xae\xd5\xe1\xdf\x06\xfc\xdb\x82\x7f\xd7\x3d\xa0\x05\xf2\x6f\x03\xfe\x6d\xc1\xbf\xeb\xf0\x6f\xbd\x4f\xfe\x6d\x76\xf2\xcd\xec\xe5\x4b\x86\xd4\x4b\xb4\xb9\x7d\x4c\x03\xb2\x23\x2a\x0e\x21\x22\x10\x24\x61\x36\x9a\xac\xf0\x32\xab\x39\x2a\xa4\xf4\x06\x13\x1f\x56\xe8\x83\x24\x61\xac\xe0\xeb\x8c\x86\x0f\x10\x5d\x3e\x0f\xe2\x23\x9c\xe2\xac\x8b\x1c\x5b\x24\x1b\x84\xe3\xcf\xe1\x94\x99\xfe\xc6\x43\x14\x1d\xc5\x70\x1c\x1b\xf9\x29\xea\x63\x1c\x81\x7b\x00\xbb\xe0\xf2\xa3\x00\x6c\xf8\x82\x30\x40\x51\x9c\x31\x3b\x4c\x93\x14\x68\x36\x17\x0e\x89\xdb\x8b\x9e\x7f\xc6\x37\x87\x49\x18\x27\x47\xd4\x04\x78\x63\x23\x7f\x6f\x25\x1d\x6e\x17\xa6\xcd\xa9\xd9\x01\x55\x7c\xe3\x7f\xdc\xe2\x70\xc3\xde\x7c\xfe\xd6\xc2\x9f\x3f\xe3\x9b\x5f\xe3\x04\xac\x18\x3f\xe3\x9b\x95\x2b\xf2\xdb\x5e\xec\x38\xfc\x82\x59\xa9\x34\xbc\x78\x4b\x18\x10\x5a\x45\xad\xa2\x65\x24\x1c\x01\x12\x18\x20\x13\x2c\x1f\x39\x8e\x63\xfe\xcc\x1b\x5c\x42\x9d\x52\x2d\x90\xfe\xa7\x83\x11\x26\xc7\x0f\x44\x44\x68\x4b\x1f\xd2\xa3\xf8\x8a\xc0\xae\xf0\x66\x96\xc8\x2e\xfd\xb2\xb0\x0f\x32\x5c\xfb\xb0\xf0\x46\xa5\x71\x96\xde\x9d\xea\x4b\x35\xb7\x11\x25\xe8\x50\xd1\x83\xfe\x7c\xc3\x30\x64\xcf\x16\x29\x04\x31\xb2\x13\xe5\xe9\x20\x59\xcb\x91\x3f\x09\x95\x53\xa8\x73\x46\x47\x16\x66\x9c\xbd\xb1\xb0\x1a\x37\xc3\x42\xd2\x7e\x62\x00\x87\x70\x3a\xfa\x50\xca\x68\xff\xc0\x10\xff\x97\x40\xdc\x89\x39\x9b\x85\xa3\x38\x43\x84\x24\xdd\x85\x32\x79\x0f\x50\xb7\x80\x42\xc8\xc7\xb3\x7e\x19\xc8\x20\x3e\x71\x98\x67\xd2\xde\x06\x1f\xf2\x9d\x8a\xc9\x68\x67\xd2\x2e\x26\x97\x58\x57\x0a\x00\xa6\x0c\x32\x7b\x3d\x07\xdb\xfd\xf0\x1a\xd8\x76\x11\xb6\xbf\x6f\x00\x13\x3f\x65\x83\xbc\x9a\x53\xc7\x57\x54\x63\xa8\x5b\x26\x1b\xe5\x13\x0e\xa4\xc5\xd6\xdd\xcf\xa8\x43\xf8\x99\x36\x61\x68\x63\x03\xb5\xe6\x4d\xda\x77\x37\xb4\xf6\x3e\x3b\x46\xdc\xb5\x66\x0c\x5a\x67\x43\x72\x86\x7e\x27\xb2\x84\xb9\x88\xe6\x72\x73\x59\xa6\x2b\x66\x33\x61\x74\xf9\xde\xc2\x69\x8c\xd7\x6e\x66\x43\x8a\xe6\xfc\x46\x3c\xe5\x2c\x87\xbf\x72\x70\x1d\x99\x61\x31\x3e\xba\x2c\xea\xd8\x88\x17\x8e\x8c\xbc\x99\x7f\x15\x10\x8d\x93\x9d\x3c\x2c\x67\x6a\x59\xc1\xcd\x43\xfc\x0d\x6a\x81\x27\x0b\x7d\x28\xa2\x7d\x75\x2e\x4e\x39\x04\x26\x69\x2e\xd8\x91\x02\x60\xaa\xd0\xad\xae\x21\x42\x8a\xaa\x70\xed\x58\x4a\x67\xe8\x77\xf7\xe2\x74\xfc\xa9\xc2\xb7\x7d\x05\xea\x08\x34\x4f\xd5\xa5\x68\x9f\x03\xa7\x24\xeb\x49\xd3\x83\xa3\x41\x72\x33\xa5\xa6\xb1\xb2\x9c\xb7\xef\xa1\x78\x38\x4c\x71\x66\xcc\x0c\x5d\x23\x41\xdc\x13\xf5\xf2\xc2\x9e\xb9\x57\x7b\xf9\x09\x31\xff\x59\xcf\x7f\x36\xf2\x9f\x4d\x0f\x58\x8c\x7c\xca\x50\x70\x0d\xf0\xa2\xb8\x12\xae\x79\xe5\x4f\x51\x23\x0a\x40\xf6\x6c\x65\x23\x87\x10\x43\xe8\x7b\xff\x94\x82\x21\xf2\x8b\x3e\xa4\xca\x37\xb5\x6c\xb3\xa0\x6c\xd3\x7a\x24\x2a\x33\x84\x2a\xad\x7a\x2a\x81\xaa\x8f\x75\xf5\xb1\xa1\x3e\x36\x3d\xa1\xb0\x30\x36\xef\xd5\x55\xb4\x47\x4e\xbe\xdf\xc5\x18\xd9\x27\x5d\x19\x26\xeb\xac\x7b\xe8\x7e\xe4\x66\x23\x1a\x76\x20\x28\x2d\x59\x5b\x06\xf6\x1d\x66\xc1\x42\xe1\x46\x92\x8a\xea\x04\x53\x8b\x8e\xab\x26\x0d\xd6\x19\xbc\xfe\x5d\x61\xb6\x35\x9b\x06\x28\xad\xeb\xd3\xa1\xd5\x32\xe6\x07\x6a\x35\xd4\x5a\x0d\xbd\x96\x55\xdb\x94\x36\xf5\xe9\xd4\x6a\x35\x6d\x6a\xa8\xf7\xda\xd9\xc1\x7e\xf4\x97\xb7\x40\xdb\x89\xe1\xc8\x72\xc6\x11\xfb\x2f\x1d\xd5\x0d\x54\x7f\xcd\x7e\xbe\xe1\x33\xc4\x5e\x38\xf6\x5d\x98\xe3\x70\x98\x01\xa5\x7b\x0e\x45\x59\xe1\xc4\x71\xd4\x33\x32\x79\x92\xba\xa6\x26\x24\xaf\xdf\x25\x45\x57\x25\xad\x1b\x72\xd7\xef\x92\x52\xab\x92\x36\x74\xa9\xeb\x77\x49\x7f\x95\x36\xa5\xd7\xc6\x36\xbc\xb4\x64\xdb\x00\x00\xb9\xba\x8a\x5c\xdd\x81\x5c\x63\x0e\x72\xcd\x42\xe4\x6a\x77\x44\xae\xa1\x22\xd7\x70\x20\xd7\x9c\x83\x5c\xad\x10\xb9\xfa\x1d\x91\x6b\xaa\xc8\x35\x1d\xc8\xd5\xe6\x20\x57\x2f\x44\xae\x31\x17\x39\x2b\xe9\x7e\x9a\x82\x11\x51\x9a\xf9\x19\x36\x0b\x00\x3b\xc9\x6a\x96\x8e\x01\xcb\xc8\x74\x3d\x1a\x7c\x21\x73\x91\x35\x6c\x5f\xc8\x40\x64\xba\x76\xdc\xaa\x44\xb1\xae\xa7\x39\xbc\x0f\x96\x4f\x85\x9e\x3c\xa4\xb5\xa3\x9f\x5a\x2c\xcb\x47\x3f\xb6\x98\x2b\x48\x39\xb7\xe4\x4b\xa8\x5a\x8e\x12\xc4\xfa\xe1\xd8\xd5\xdd\xd8\x99\xeb\xc7\xc0\xce\x58\x42\x2a\x76\xb5\xbb\x60\xd7\x90\xb0\x6b\xb8\xb1\x33\x17\x90\x81\x9d\xb1\x86\x54\xec\xea\x77\xc1\xae\x29\x61\xd7\x74\x63\x67\xae\x20\x03\x3b\x63\x11\xa9\xd8\x35\xe6\x63\x67\x52\x2b\xe6\x91\xad\xed\x72\x09\xdd\x86\x2d\xeb\x48\x17\x72\x8c\xe5\xa4\x6e\xae\x96\x55\x65\x88\x3e\x4d\x97\xec\xc3\x8e\xc2\x5d\xd4\x68\x77\x56\x9b\x0d\xa6\x81\xae\xda\x54\xc1\x5c\x62\x11\x02\x52\xca\x3c\x87\x99\x6a\xf8\x45\xca\x12\x3e\x21\xc8\xe1\x3d\xf4\x07\x58\xe8\x88\x05\x90\xff\xc4\xd7\xfe\x64\x2a\x4e\xca\xf9\x07\x3e\xa7\x14\x56\x86\xaf\x33\xe9\x76\x7b\x65\x73\xfb\x78\x85\x9d\x23\x2a\x13\x6e\x92\xfe\x19\xdf\x78\x68\x30\xbc\x10\xd2\x7c\x0e\x65\x3a\xf6\x09\x12\xd7\x19\xd2\xa1\x30\x09\xbf\x92\xb7\x63\x03\xc4\x74\xda\x3d\x8b\x12\xfb\x9c\x86\x4d\xdd\xc5\xe3\x29\x4e\x2a\x9b\xdb\xf4\x5a\x9f\xea\xec\x9f\x3f\x63\x36\x2b\x72\x93\xaf\x9f\x3f\x87\x10\xb8\x60\x40\xa2\x58\x15\x74\xdb\x0d\x8f\xdb\x25\x74\xdb\x60\x3b\x22\x59\x26\x74\xdb\x2d\x2f\x37\x49\xe8\xb6\xc1\x87\x71\x12\xb4\x7f\xec\x76\xea\xb7\x67\x5e\xbb\x71\x2f\x6b\x91\x6f\x69\x26\xf2\x68\xc6\x1c\xdf\xd0\x2c\x83\xae\x84\x97\x88\x19\x50\x90\xe6\xd1\x20\x9e\x4c\xe3\x08\x62\xae\x93\x6f\xab\xcf\x9f\x89\x79\x1f\x87\xfd\x15\x56\xf4\xeb\x57\xd9\x00\x40\x78\x7d\x3e\xb0\x71\x87\x9f\xe2\xdc\xaa\xc3\x4f\xb1\xf4\xed\xd7\x38\x09\xc0\x2f\x5d\x14\x10\x6f\x64\x08\xb3\x21\x18\xfc\x01\xad\x6f\xf2\x5b\x9e\x1c\xa6\xf5\xb3\x82\x19\x06\xd7\xaa\x1e\x59\xa8\xd2\xfb\x4f\xd9\x70\x1d\xa0\xe0\x68\xb0\x42\x1e\x34\xac\x3b\x2d\xf1\x95\x3e\x16\x19\xa2\x88\x2f\xdb\x97\xd3\xf7\x5b\x3b\xf9\x65\x13\x7d\xb6\xde\x60\xf5\x53\x6a\x9f\x47\x96\x15\xbf\xc5\xca\xf0\x64\x3a\xf6\x33\x1b\x83\x12\x51\xa6\xff\x8c\x58\x44\x1e\xae\x41\x05\xaf\x02\xc1\xeb\x40\xef\x17\x7e\xc1\x2b\x3c\xc2\x64\x17\xb5\x50\xa5\xde\x58\x47\xfd\x30\x4b\xab\x45\x00\xc3\x4b\x0b\xbc\xbd\x5f\xee\x0a\xee\x7c\xfb\x63\xef\xfc\xb7\x9d\x83\xa3\xfd\xf3\xfd\x83\xad\x6d\xb4\x09\xb1\x0d\x32\x3f\xca\x50\x82\xa7\x09\x4e\x71\x94\x85\xd1\x05\x57\xc4\x10\x32\x9c\xc4\x41\xde\x77\x2b\xcc\xad\xed\x52\x30\x19\x3b\x35\x60\x4a\x97\x82\x9a\xc9\x91\x78\xb4\x53\x94\xe5\x92\x30\x9f\x4d\x8a\x6e\x0f\xfc\xbe\x67\x09\x18\x3c\x88\x24\x1f\x72\x11\xa5\xb8\xd4\x3b\x41\xf7\x64\x0e\xd0\xc9\x08\x93\x51\xcf\x62\x34\x63\x7e\x02\x84\x05\x20\x52\x18\x40\x2b\x20\x57\xf3\x87\xc1\xf0\xa2\x0b\xa4\xcb\x71\xad\xca\x3b\xaa\x81\x2d\x6c\x17\x29\x85\xcd\xc8\x2f\x8c\x5c\x93\x61\x43\x9f\xda\x63\x4a\xb8\x13\xd2\x23\xc8\x7f\xc6\x37\x2b\xd6\xb2\xdc\x35\x74\x30\xbc\x40\x95\x03\x68\xc5\x1f\x57\xa1\xce\xc0\x36\x78\x25\xc7\x40\x6d\x8b\x07\x12\xa5\x13\x7a\x4b\x48\x84\xf7\x8e\x10\xca\xa0\xa8\x4f\xe4\x5c\x11\x0e\xdc\xdf\x55\x29\xc1\x2c\x80\x14\x69\x41\xde\xe3\xf9\xd5\xf3\x0a\xdd\xa6\xb7\xe9\x30\xc7\x49\x85\x5d\x9e\xc1\x10\x7a\xe8\x4f\x14\x5e\x76\x51\x78\x99\xf3\xc6\x5b\xc5\xf4\x40\x99\x6f\x15\x52\x57\x89\x0b\xc5\x24\x07\x5d\x03\x20\x67\x0e\xa1\xf5\xd9\x8d\xb3\xba\x56\x2d\xb2\x87\x2e\xa1\x95\xa4\x27\xc7\x42\x7c\xa2\xa7\x87\xa5\xa7\x2d\xfc\x50\xf4\x24\x20\xdd\x8f\x9e\x54\x3e\x7d\x07\x7a\xda\x8b\xc2\x2c\xf4\xc7\xe1\x17\x9c\x22\x1f\x45\xf8\x6a\x7c\xc3\x30\x0c\xd8\x70\xcc\xa7\x25\xbe\x6b\x5c\x0f\xe3\x64\xb2\x1f\x07\x18\x6d\x53\x67\x35\x88\xd3\x9c\x73\xba\x38\x91\xe9\x14\xac\xab\xc1\xcf\x8f\x53\xad\xd8\x64\xec\x64\xf8\xdd\x91\xec\x83\x91\x55\xc5\xfc\x60\xe3\x14\x77\x24\xb8\x30\x0a\x15\x0b\x1b\x31\x4d\x12\xb9\x58\x54\xd4\x9b\xd3\x29\xa1\x05\x18\x2d\x9e\x6e\x3a\xb5\x5c\x33\x90\x21\xde\x10\x3f\xf9\xa6\x48\x69\xd0\x3c\x15\x67\x44\x72\xa6\x86\xf5\x71\x32\xa1\xd3\xee\xdb\x74\x37\x94\xbe\x73\x92\xda\xc8\xc9\xeb\xb5\xad\x24\xb5\xa3\x01\x5b\x19\xeb\x59\x3c\xa4\x84\x4e\x3d\x00\x6c\xfd\x00\xfb\xa2\x4a\xe9\x85\x03\x36\x3a\x2a\x1f\x86\x60\x0e\xa9\x68\x09\xb4\x67\xf7\x24\x1f\xb6\x04\x4d\xdc\x94\x19\x4e\xca\x18\x51\x51\xa3\xa2\xc0\xcf\x7c\xd4\x07\xd9\x4b\x2d\xe1\x90\xc7\x00\x34\x4d\x75\xc1\xdd\x9d\x75\xc0\x87\x38\x81\xb9\x1c\xc4\xd1\x20\xc1\x19\x5e\x66\xc3\x31\x8e\x2f\x14\xa6\x2c\xdd\x4b\x1d\x2d\x36\xd6\x10\x50\x03\x30\xa7\xfe\x2d\x8c\xa7\xe0\x40\x62\x29\x38\x58\x60\xd3\xfb\x9a\x32\x57\x18\x02\x94\x29\x3b\x09\x6f\xe0\x6d\xb0\x06\x24\xf0\x25\x76\x2e\x89\x3f\x09\x58\x34\x6a\x16\x8b\x46\x10\x46\x17\x0f\xc0\x4d\xf2\xce\x6f\x70\xf2\x60\xf0\x2b\x2f\x48\x9b\x2f\x54\x32\x29\x53\xef\x8a\x63\xee\xa4\x30\x56\xb2\xab\x85\x79\xa5\x43\xe7\xe0\x1e\x38\x0a\x6c\xb3\xef\xc3\x17\xb9\xba\x8d\xa6\x68\x7b\xc8\xbf\xf4\xc3\xb1\xdf\x1f\x63\x6a\x86\x98\xba\xb7\xc5\x73\xde\x99\xd2\x54\xb5\x13\x46\x6c\xe3\x2b\xdc\xa7\x18\x5c\x75\x9f\xf9\x18\x67\xcc\x3d\x9a\x46\x4d\xa3\x90\xf2\x5d\x03\x85\x29\xc2\xc3\x21\x1e\x64\xe1\x25\x1e\xdf\x20\x1f\x05\x38\xcd\x92\x19\x3c\x7b\x28\xc1\x7e\xb0\x1c\x47\x03\x5c\x6a\x9f\x29\x4b\xbd\x80\xc6\x63\xd1\x30\x05\xfe\xd8\x94\xcc\x47\xb2\x52\x9e\x88\x45\x95\x45\xa9\x5f\x54\x9c\x4f\xfe\xbc\x68\x79\xfa\xdf\xc9\xe7\x62\x06\x85\xd4\x12\xe1\xb0\x10\x00\x2a\x5c\x2d\x4a\x51\xcb\x45\xc9\x02\x0c\x19\x02\x22\x11\x54\xd9\x82\xc3\x01\x0b\x98\xc9\x39\xf5\x8e\x34\x21\xd6\xc5\x67\xd6\x9e\xab\x6c\xae\x37\xd6\x57\x9b\x0d\xf9\x13\x55\x89\xd8\xbe\x68\x72\x50\x17\xd5\x95\xaf\xaa\xfc\xdb\x45\x8d\x32\x67\xa7\xd4\xaa\xca\xf6\xe7\x2b\xb2\x91\x73\x6d\xf2\x53\x0b\x1b\xe9\x93\x11\x96\x84\x02\x96\x69\xcb\x47\x23\xd0\x1a\x13\x21\xb3\xc4\x52\xe4\x22\xec\x66\xc4\xf1\x81\x08\x03\x7c\x59\x13\xa1\x89\xad\x6b\x4b\x87\xbe\xc1\x61\x89\x59\x7b\x9b\x2a\x4f\x4d\x47\x6e\xc8\xb6\xce\x55\xa6\xd4\xeb\x3a\xfd\xa6\xc8\x9f\xf8\x94\xe2\x31\x1e\x64\xb4\xe1\xe3\x2c\xf1\x33\x7c\x71\x53\x71\x99\x6b\x4b\xda\x67\x10\x17\x37\xd0\x0b\xca\x4a\x5f\x38\xcd\xc3\xd8\x6c\x1c\xfa\x69\x4a\xd8\xc4\x5b\x3f\xc5\x81\xe2\x31\x27\xff\x15\x1b\x87\x31\x50\xc7\x38\x81\x03\x17\xd9\xd5\xdc\x90\x8a\x17\xb9\x9e\xdc\x8f\xdd\x67\x14\xd8\xa8\xbb\x90\x62\xe4\x24\x33\x36\xf3\x86\xa5\xc8\x6e\x34\x8f\x02\x66\x9f\x07\x71\x71\x43\x51\xf4\x90\xfb\x02\x47\x1f\x03\xcf\x61\xe9\xc9\xc8\xbe\x6b\xf4\x5f\xbb\xcf\xb9\x17\xda\xea\x4d\x91\x87\x0a\x6f\x8c\x74\xcc\x2d\x13\xaa\xb3\x6d\x99\x4b\x56\xaa\x4c\xc3\x6b\xbf\x7a\x53\x75\xd8\x69\x96\x60\x7f\x72\x27\x55\x36\xc8\x50\x4c\xf9\x2c\xdb\xe0\x37\x1b\xcb\xfd\x90\x1a\x6c\xab\x27\x1a\x2a\x9d\x40\x1c\x6b\x49\x33\x5d\x47\x95\x66\x43\x55\x4c\x4b\x0a\xdf\x63\xc0\x4f\x53\xfb\xea\x2f\x0b\x3c\x42\x76\x2c\x7b\xad\x6d\x87\xe5\x22\xe2\xd4\x4f\xe0\xb8\x65\x13\x10\xcd\xed\x0d\x8e\x37\xb9\x75\x15\x17\x1a\x7f\xf8\xe1\xc5\x70\x3c\x4b\x47\x2f\xca\x6d\x73\x14\x8a\x6b\xa3\x13\xc3\xdc\x45\xf5\xa2\x79\x85\x73\x2d\xa4\x35\x9d\xca\xb7\xa5\xb2\xf2\xfc\x7c\x42\xcf\xbe\xbd\x15\xf6\xe3\xcf\xdb\xf9\x14\xa2\x78\xec\x40\x3d\x83\x4a\xa4\x36\xa4\xdb\x4d\x76\xd0\x36\x9c\x83\xd9\x7b\x59\xe9\x5d\xa4\xa0\x97\x55\x94\x13\x9e\x9d\x2b\x97\xaf\x17\xde\x4d\x37\xd5\x1e\x59\x15\x82\x7a\x6a\x99\x5c\xc1\x0f\x54\xfd\x0d\xf6\x43\x3e\x53\x7c\xbb\x03\x3d\x6c\xef\x6d\xcf\x50\x45\x73\x8e\x12\x5e\x52\xaf\x9d\xbb\x68\x9e\x73\x18\x85\xba\x42\x51\x97\x2b\x9a\xa4\x7a\x77\xd2\x38\x8b\xe9\xcc\x0f\x48\xff\x33\xa7\x33\xd7\x04\x2f\x38\x9d\x56\xc5\x6f\xc9\xe9\x14\x75\xef\x31\x9d\x45\x0a\xdf\x72\x57\x07\xdf\x74\x3a\xef\x3d\x5d\x05\x4b\x60\xce\x7c\xe9\x7a\xd3\x82\x49\xa2\x9b\x89\xd0\xf3\x0e\x6c\x62\x1d\xb3\xba\xbe\x44\x1b\x28\xbc\x94\x67\xab\x68\x8b\x60\x3b\x26\x0d\x2c\xdd\x1b\xf9\x61\x04\x39\x4f\x5c\x77\xad\x6f\xc1\x6e\xe0\x9c\x77\x1e\x6d\xb8\x83\x0f\xe8\x2a\x36\x65\x07\x21\x75\x0d\x62\x90\x86\x26\x6f\x4c\xdb\x25\xc4\x9d\xe8\xeb\x22\x8e\xf2\xb6\xc7\xb7\x03\xed\x24\x24\x35\xa1\xcc\x1d\xe9\xd5\xdb\x9e\x65\xef\x31\xc1\xd3\x26\x0e\x45\xfc\xcf\x8c\xab\x31\x28\x95\xfa\x19\x33\xea\x5e\xd1\xeb\x18\x30\x34\x9a\xa5\xd2\x91\xd0\x8a\x30\x61\x29\xe6\x32\x12\x52\x39\x21\xb2\xde\x90\x30\xbb\x2c\x02\x84\xfd\xbc\x1a\x61\x16\x7a\x9f\xe2\x07\x91\x3c\xd3\x12\xc8\x99\x0b\xc3\x5e\x90\xfc\xc1\x54\x32\x51\x87\x7a\x03\x40\x7e\x3c\xe8\x82\x70\x6d\xd0\x65\x59\x79\x32\x50\xae\x02\x34\xcc\xe4\x55\x28\x4e\x5b\x68\xab\x03\x2c\xd2\x6f\x48\xe4\x85\xe4\x30\x9c\xcd\x85\x58\xa1\xc9\x11\xaf\x1c\xe6\xac\xbf\x1d\x1c\xc1\x79\x99\x11\x9d\x59\xe6\x3a\x4e\xa0\x5f\xb9\xa2\xdb\x43\x4a\xbf\xbc\xbc\x59\x9b\xd0\xcf\xf0\x90\x7d\x5d\x2a\xfa\xe8\x5a\x31\x3b\xc2\x13\x0c\x52\x38\xec\xae\x94\x04\xd8\x55\x14\x9c\xf6\xc1\xa1\x1d\x5e\x9b\xd5\xb9\x04\x8b\x2f\x79\xdc\x79\xca\x4c\x69\x42\x79\x8e\xb7\x30\x05\x74\x76\x40\xf6\xdc\x99\xbb\x6e\x03\x5c\x62\xdd\x8a\x7d\xea\x69\xdd\x3e\xad\x5b\x74\xf7\x75\x7b\x9f\xd5\x01\x16\xc2\xa3\x30\x5d\x78\x6d\x58\x31\x61\x14\x0d\x5c\xe4\xb7\x83\x23\x27\x07\x90\x3d\xc8\x0c\x0e\x70\x5f\xb6\x63\xc5\xec\x24\x1f\x9a\x3e\x1e\xc4\x13\xb6\x74\x08\x5b\x08\xe3\x59\x5a\x9e\x79\x88\xc1\x2a\xcb\x1e\x04\x29\xf1\x6e\x54\x9c\xb8\x2f\xe4\x01\x05\x22\x12\x97\x96\x6c\x1e\xfe\xa3\x38\x4e\x31\x9a\x84\xd7\x44\x16\xb2\xf4\x0f\x3c\x41\x4d\x21\x0d\xc9\x84\xc8\xa4\x30\x17\xd9\xc5\x97\x20\x9d\x92\x93\x4e\x3a\xeb\xa7\xf8\xbf\x67\x38\xca\xac\x2a\x06\xa4\x8a\x76\x52\x5a\x0f\x75\x14\x9d\xaa\x41\x19\x25\x6d\x56\xe6\xab\xfa\xc9\xce\x66\xc3\xca\x16\x23\x29\x5f\x6d\xd6\x48\x49\xe4\x0f\x26\x30\xb7\x1e\x0f\xcf\xd0\xef\x1b\xb4\xde\x69\x58\x18\xba\x24\xff\xcd\x4d\xa0\xdf\xf6\x58\x79\x25\xa0\x89\x24\xda\x1e\xfa\x41\x40\x26\x70\x8e\x02\x64\x0a\x69\xae\x7a\x2b\xf4\xbf\x76\xf5\xc7\xe1\xfb\xde\x31\xfa\x5f\xed\xd5\x35\x34\x65\x40\x53\xa6\xcb\xb3\xc1\x3c\xfc\x3c\x48\xd7\x40\x4e\x9e\xfa\xc1\x0a\x7f\x2a\x90\x8d\x0f\x7d\x7e\xfd\x3c\x4b\x79\xec\x7c\x11\x08\x85\x99\x2b\x43\xe0\x64\x81\xc7\x42\xf6\x57\x00\x59\xbe\x7d\x26\x68\x59\x2b\xd9\xf5\x78\x2c\x04\x94\x74\x1f\x09\x80\x52\x11\xcd\x92\x0c\x0a\xc4\xb3\x7c\xe4\x63\xb3\x38\x7c\x89\x71\x25\xbf\xf2\xeb\x35\x4f\x8b\x9b\xa5\x5c\x30\xfb\x81\x7e\xb9\x76\x67\x06\x22\xaa\xd1\x58\x27\x1b\xd2\x78\xb9\x62\x86\xcc\xa2\x4c\xd0\x0e\xf8\x15\x99\x50\x23\x46\xb0\x06\x50\xfa\x62\x99\xe6\x9c\x16\x11\x56\xfe\xa5\x15\xb0\x35\x4b\xef\x85\x78\xbb\x66\xe8\x05\x9a\xea\x0d\xbe\x12\x7a\x81\x08\x28\x0a\x16\xb9\xaf\x8b\xf1\x9e\x39\xb8\x18\xef\xc1\xad\x45\x79\x3b\x17\xb3\x42\xa4\xd2\xe2\xf0\x05\x39\xfb\x51\xdb\x44\x21\x5a\x72\xb9\xe5\xcb\xd0\x69\x9c\x7b\xe9\x4d\x81\xf4\xaa\x61\x87\x36\x72\xdb\x77\x7e\xf8\x97\x41\x7b\x2a\x4a\x36\x33\x84\xcd\x20\xb0\x0f\x02\xcc\xf5\x20\x8e\x06\x7e\xc6\x61\x96\xd6\xc0\x7c\x8a\xa6\x82\xa1\xc0\x92\x1d\xf9\x01\x0d\x64\xc4\x16\xea\xb7\xe1\x32\xb3\x48\xe7\x33\xdf\x84\x23\x40\xb3\x25\xae\xdc\xa1\x9c\xce\x12\x6c\x7c\xe0\x1d\xce\x94\xcc\xc5\xd2\x22\x86\x18\xb0\x68\xec\xa7\x19\x3c\xcf\x5f\xd3\xb9\x78\x7d\x5a\x51\x97\xf3\x32\xaa\x57\xa9\x8b\xd9\x19\x73\x06\xb3\x79\x12\x53\xc1\xc1\x4d\x31\x39\xb8\x0d\x7d\x0d\x4a\x9b\x29\xdd\x36\x17\xd4\xf3\xff\x15\x17\x41\x36\x17\x05\xfb\xcd\x82\xed\x56\xa1\xe8\x1e\xe8\xe1\x8c\xfe\xf7\xe3\x00\xdf\x52\xf5\xe0\x89\x38\xad\xd1\x4b\x11\x38\x49\x48\xdd\xe9\xbd\xed\xb9\xa0\xb0\xb9\xba\x15\xf4\x45\x60\xe9\xc2\x86\x09\x11\x48\xde\x41\xe0\xe0\x47\xc0\x06\x40\x32\x9c\xd4\x08\x9c\x60\x0a\x98\x79\xda\xa9\x8e\xb6\x6d\x34\x71\xab\x78\x23\x2c\x60\x18\x48\x27\x5a\xfd\xd8\x93\xac\x0f\x8b\x6d\x00\x0b\x02\x9c\xa9\xf6\xa1\x16\x3f\x4e\x90\x9b\xc9\x08\x28\x6a\x51\xa4\x2a\x76\xc9\xf7\x09\xd8\x7e\x3a\xf0\xcf\x27\xd6\x3c\x0c\x18\xb6\xa4\x5c\xd2\x56\x8d\x4b\x9c\x27\x06\x02\x15\xb6\x44\xd0\x68\xc0\xa9\x5c\xbb\x9b\xb1\x4b\xfb\xab\x2f\x8b\x9b\x57\xad\x57\xaa\xe8\xe5\xea\xc2\x18\x08\x55\x8b\xe3\x2c\xf3\x1e\xe3\x29\xf2\x33\x34\xc6\x84\x0b\xc6\x11\x5f\x01\x2c\xcd\x07\xb5\x04\x85\xfd\x1a\x18\xae\xc9\xb7\x90\x38\xdf\x4c\xc2\x88\x1a\x89\xb2\x43\xbc\x11\x2e\x51\x7d\x64\x95\xe8\xf4\x49\xf8\x53\x42\x9a\x80\xfd\x31\x3d\xf2\x86\x97\xe8\xa7\x9f\xac\xfa\x78\x3d\x50\xc7\xe1\x9d\x74\x19\x39\x26\xaa\x32\xc5\x79\x3e\xd7\x9b\x2d\x7b\x25\xed\x16\x49\x73\x91\x44\x18\x4a\xb3\x57\x16\x82\xe6\xcd\x3d\x2c\x21\xaf\xae\x92\x83\x0c\xcd\xf7\xe5\x12\xb9\x40\x5e\x67\xa6\x5f\x20\x81\xc3\xef\xb9\x3a\x08\x7e\x15\x4f\x6d\x04\x5d\xa7\xe4\x3b\x5d\xc6\x3f\xde\xb2\x7a\x5c\xbc\xad\xed\x81\xe4\x37\x67\x06\xa8\x7c\x64\x6b\x6f\x9e\xe5\xdf\x3d\x2d\x15\xc0\xf4\x8e\xc9\x1e\x76\x33\x14\x34\x88\xc7\x63\x4c\xe9\x3f\x1e\x72\xd1\x00\x44\x4d\x0c\xc9\xf4\x8a\x44\x0f\x49\x14\x95\x9c\xbc\xc9\x36\x9a\xf8\x57\xd2\x2b\xab\x5f\xa2\xdd\xf5\x83\x3a\xa0\x0b\x21\xa5\x4c\xed\xfc\xe2\x11\x52\x3c\x30\x2e\x48\xeb\x93\xf5\x69\x98\xe3\xba\x00\xa5\xfe\x98\x62\x0f\x3f\x00\x18\xa8\x24\x7d\x1a\x7e\x14\x27\xe1\x25\x95\x55\x38\xc7\xb0\x02\xe4\x57\xa9\xb9\x9c\x2f\x59\x0e\x9a\xb1\x56\xcb\xc9\x35\x77\xe9\x59\xb1\x7c\x33\x18\xe1\xc9\xdd\xe0\xda\x05\x4e\xa6\x32\x07\x8b\xe9\xa1\x04\xcf\x0a\x82\x26\x65\xbc\xcd\x93\x36\xd2\x53\x0c\x15\xb1\xf8\x5b\x5d\x0c\x1b\xc4\xd1\x25\x4e\x32\x45\x86\xa5\xe9\xee\xb8\x31\x25\x58\x7c\x52\xeb\x3f\xb7\xdb\xea\x21\xad\xa2\x3a\xaf\x8a\x97\x25\xed\x61\xe6\xbb\x58\xa9\xa8\xcd\x3f\xd6\x09\xef\x26\x19\x1f\xcd\x4e\xd4\x8f\x44\x16\xab\x69\x9c\xa6\x61\x7f\x8c\xdd\x2b\xd6\xd2\xd4\x62\xce\x4d\xf9\x40\x99\xf6\xa0\xf4\x1b\x3f\x81\xff\x69\x40\x41\x42\x7d\x4e\x56\x70\x57\xfa\x9d\x3b\x3c\x59\x2b\x7d\xc6\x37\x5d\xd5\x2f\xca\x5a\x4c\xf3\x94\xb2\x17\x22\xcb\xb8\x0b\xff\xce\x29\x28\x56\x65\xd7\x74\xe7\xb2\xd7\x60\x22\xbc\x6e\x99\x60\x2f\x2c\xe4\x7a\xf5\xe8\xfc\xbe\x77\xbc\x66\xaf\x20\xb1\xf0\x96\xbd\x84\x58\x38\x12\x50\xfa\x6e\xe5\x60\x8a\xa3\xe3\xe3\x0f\x46\xb5\xf2\xce\x64\xf2\xf4\xdb\x05\xaf\x49\x78\xbd\x17\xa9\xe5\x4a\x9b\x1e\xd1\x55\x9c\x2e\xb6\x8c\x91\x73\xdd\x98\xac\x44\xf3\x0d\x74\x70\x13\x72\xa8\x73\x03\xe7\x06\xb6\xdc\x2b\x03\x76\x05\xf8\x1d\x0e\x43\x7d\x8d\x17\xc0\x81\x2c\x60\x29\x4d\x01\x06\xe9\xe3\x70\xee\x45\x99\x63\x1c\xc5\xf4\x8d\xc6\x00\x59\xd2\x7e\x5c\xc4\x3d\xca\x2e\x69\x8a\xbc\xb8\xa6\x63\x6b\x7b\x09\xbd\x78\x61\xf7\xad\xb0\x96\x5f\xc9\x62\x9a\x6f\xc8\xe5\xca\x31\xa7\x96\x83\x54\x9d\x84\xc9\x2b\xca\xc4\x29\xc6\xc6\x65\x55\x95\x97\x40\x5f\xbf\x52\x72\xcd\xeb\xac\xf0\x49\xbc\xe1\xc7\x5e\x43\x47\x63\x95\x93\x28\x95\xcd\xbb\xd7\xa0\xed\xc0\xd5\x86\xf8\x69\xbf\xdd\x60\x3d\xb7\x11\xa7\x0d\x34\x2b\x2e\x72\x19\xc3\xee\xa5\x0e\x62\xf1\x75\x87\x58\x75\xbe\x7b\xc9\x45\xbc\x99\xe5\x41\x3c\x99\xfa\x19\x6c\x2f\x65\x97\xa1\xbc\x2d\x68\x9b\x98\x24\xfe\x94\xdd\x13\x6d\xcb\xef\x2e\xc8\x3d\x94\xe1\x60\x4c\xdb\x3e\xe6\xe4\xed\x20\x64\x89\xba\x5c\xbc\x51\xa1\x6f\x51\xbc\x34\xf7\x9d\xa3\x96\x91\x23\x2d\x29\x4b\x30\xff\x62\x0b\xd4\x48\xc4\x5d\xad\x02\x79\x67\x3b\xc6\x42\x7f\xcd\x43\x2c\x29\xee\x54\xb5\x5c\x49\xd1\x6a\x0c\xed\xfd\x69\xed\xba\xdd\xec\xd4\x3b\x83\x35\x48\x6c\xd0\x69\x77\x5a\xed\x61\x7b\x78\x56\xe5\xaa\x78\x00\xcd\x1f\xf2\x7e\x38\xce\x91\x25\x50\x70\x8e\x85\xe3\xf0\x25\xea\xe6\x8c\x8c\x86\xb5\x59\x7c\xcf\x2b\x5a\x63\xb2\xbf\xd2\xa2\xc2\x23\x5f\x27\x39\x9d\xde\x79\xc9\xa8\x31\x1b\xf8\x82\xbe\xc3\x1a\x7e\xd8\x00\x0e\xa6\x30\xaa\x2d\xbd\xa9\x9f\xa4\xb8\xa2\x2c\xd4\x82\x8b\xc9\x24\x55\x14\x3f\x79\x35\xab\x57\x02\x29\x8e\x68\x0c\xaf\x39\x8b\x8e\x12\x86\x81\x4c\x91\x7a\xb5\x08\x22\xbf\x8c\x93\x0e\xc3\x2c\x29\x84\x01\xee\x04\xa7\x19\xb5\x6d\xf0\xc7\x96\x05\xaa\xc1\x3c\xad\x9d\xa1\x8d\x0d\x94\xaf\x3d\xf4\xd3\x4f\x7a\xbb\xa7\x75\x56\x86\xaf\x49\x97\x0a\x6a\xfb\x9a\x5e\x60\x98\x2d\x23\x95\xc3\x18\x8b\x5f\x6b\x91\x99\xf2\x34\x3c\xd4\xaa\x16\x58\xd7\xc5\x97\xec\x88\x0e\x57\x41\x39\x0c\xb3\xbc\x01\x7f\x0a\x0d\xd4\xf4\x5b\x6b\xa3\xb8\x72\xab\x53\xef\x94\x63\x14\xd6\xa3\x91\xe3\x18\xe4\x49\xa7\x13\x55\x34\x2f\xbc\x2b\xe2\x8b\xf0\x2a\xf1\xa7\x53\x90\x23\xfd\x8c\x35\x2f\xab\x4c\x90\x4f\x76\xfa\x54\xf2\x4a\x2b\x5c\xbd\x8a\xab\x8f\xe1\xca\x96\x3b\xfc\xd8\x3e\x95\x75\x20\xb9\xf3\x65\x8f\x10\x7a\xb8\x8c\x5f\x24\xd5\x73\x1d\x81\xdc\x5b\xd6\x59\xea\x10\x1a\x05\x94\x6a\xc4\x01\x23\xbf\xd8\xb1\x1c\x9c\x8a\x42\x44\xe9\xde\x8b\x80\x50\xd7\x10\xd5\xa4\x89\x2d\x0c\x2a\xc5\xae\x1d\xc8\xbc\x31\x6f\xba\xfb\x78\xa8\xe6\xca\x27\xcb\x51\xa7\xc0\xfb\x9c\x35\x4d\x6d\x50\xd8\xef\xdc\xef\xfc\x6f\x12\xc3\xc5\xbe\x85\x6d\xfe\xb5\x1b\x18\x59\x96\x76\x8d\x8a\xb9\xac\x84\x7f\xa5\xa9\x8d\x50\x5c\x2d\x1d\xa7\xb0\xc7\x6b\x30\x0f\x52\xa3\xab\x13\xbe\x69\xe3\x9e\x58\x6d\x0e\x69\xa0\x40\xd9\x61\x71\x8e\x75\x7b\xb1\xde\x2d\x84\xce\x42\xd1\x73\xb6\x6d\xf6\xeb\x52\x74\x83\x38\x77\x3e\xb1\x05\x40\xb3\xfa\xac\x1a\x62\x49\xee\x99\x21\x02\x24\xb0\xce\xde\x45\x32\xe9\x41\xff\x72\x98\x70\x05\x6c\x40\x61\xf6\x46\x84\xe3\x0a\xc7\x5c\xd7\x7e\x54\x7e\x3b\x2d\xda\xb4\x95\xfd\xd5\x2c\xc8\x55\x8b\x96\x4f\x84\xac\x44\xdf\x56\xc2\x4b\x4b\x11\x49\x47\xc8\xe8\xc5\x2c\x43\xb5\x82\x39\x20\xb8\x10\x35\x8b\x09\x7d\x60\x5e\x92\xbd\xb2\x14\x96\x74\x81\xba\x85\xb5\xa5\xb4\xa4\x17\x24\xa4\x37\xb4\x1c\xd7\x6e\x4b\x1f\x5b\xd8\x3d\x74\x2a\x26\x4e\x28\xbe\xe4\x6b\x19\xf4\x68\xdb\x93\x4c\x00\x62\x87\xd2\x2e\x9a\xa4\x47\xc8\xed\xfd\x77\xdc\xa7\xb4\x00\x2d\x22\xd2\xf1\x37\xd8\x9b\xf2\xa8\xca\xf3\xd9\x34\xf7\x9e\xb7\xb0\x69\x4e\x76\x2c\x8c\x82\xe4\x51\x7f\x67\x96\xfd\xd0\x28\xea\xfb\xd2\x03\x6e\x29\xce\xd8\x05\x8e\x08\x03\xdf\x60\x57\x61\x1a\x07\x49\xb5\x20\x2f\x26\x0d\xb0\xbc\x53\xb0\xdb\x6f\x38\xbf\xca\xc8\xe7\xdc\xc4\xd6\x1c\xe3\x14\xe6\x86\x21\x4f\x9e\xb2\x89\x29\x51\x17\xe9\xb0\xe4\x7b\x93\xc4\x64\x14\x85\x8f\x75\x9b\x10\x4d\x2c\xac\x8d\xb1\xb2\x35\x7d\xac\xd4\xfb\x17\xd0\x31\xf9\x69\x3a\x9b\xe0\x40\xbd\x4f\xf4\xc7\x09\xf6\x83\x1b\x69\xbf\x53\x0e\x64\xb3\x88\xa6\xad\x2c\x11\xd1\x6c\x31\xb6\x67\xe7\x5f\x0b\x1d\x9a\x08\xe3\x02\x13\xf5\x24\xc5\x0b\xf3\x7a\xb7\xbe\x68\x16\x2d\x0a\xeb\x2f\x94\xb8\x0d\x92\xa7\x2a\xa4\x03\x4e\x05\x48\x10\xbf\x9d\x07\x9c\x1b\x3a\x25\x79\xf5\xb0\xca\xb6\x54\xde\x2c\x76\x8d\xbc\x08\xe7\x84\xb0\xe1\x36\x21\x94\x3d\x99\x4b\x55\xbf\xd8\x40\x85\xda\x51\x06\xad\x40\x29\x6a\x68\x26\xac\x37\x24\xef\xed\x26\x12\xf3\xae\x4c\x3e\x07\x43\xb8\x2f\xa1\xff\x2d\xbe\x2c\x99\x67\x85\x61\x5e\x98\xbc\xa7\xd0\x49\x2b\xe5\xee\x49\xb6\x08\x78\xb8\xd3\x27\x8d\x91\xb5\xbc\xf7\x0b\x57\x18\x4c\x59\xbc\xa0\xf2\xea\x58\x5e\x83\x59\x5e\xb0\x07\x90\x53\x48\x33\x00\xb8\xd8\x2b\x24\x0f\x54\x8e\xa9\x6d\x45\x18\x31\x4b\x5e\x66\x07\xc0\x4c\x66\x2e\x70\x04\xc6\xbc\xc5\xd0\x44\x94\x72\x07\x30\x1a\x3a\xbb\x18\x96\xa9\x33\x00\x15\x96\x24\x24\x6d\xa2\x4e\x0b\x4c\x8e\xe1\x03\xb7\x9f\xdd\x1b\xa2\x78\x12\x12\x19\xc1\x43\x3e\xfd\x74\x15\x8e\xc7\xa8\x8f\x45\x83\x01\x4a\xfc\x28\x88\x27\xe3\x9b\x07\x3a\xdc\x53\xab\x09\x36\x4c\x1e\xda\xfb\xc5\x83\x29\x25\x8d\x7f\x03\x2e\x44\x27\x39\x30\x59\x90\x44\x8d\x2b\xf8\x1a\x0f\x66\x19\xae\xbc\xe0\xd1\xa8\x5e\x78\x2c\x71\x87\xc7\xcc\xb7\x1c\x62\xd1\x03\x41\xf7\xd0\x0b\x32\x1c\xe4\xff\x5f\xb8\xcf\xcc\x14\x8c\xcc\xdd\x38\x35\x7b\x9c\x44\x3d\x46\x5d\x54\xb1\x69\x37\xea\xa7\xd3\xcc\x66\xd9\xa1\xa8\xfe\xc1\x79\x95\x64\x28\x91\x29\x9c\x4a\xa7\xb5\x6a\xa4\x35\xb7\xb8\xd5\xd1\xa5\x2d\xad\x6b\x53\x5a\xa1\xf1\x66\x69\xe2\x81\x5c\x81\x2b\x62\xdc\xe5\x69\x90\xd9\x42\xba\xad\xae\xb0\x44\xde\xd2\x78\x00\xfe\xd6\x80\xb5\x84\x36\xb3\x62\x0c\xc0\x6e\xda\x50\x93\x8b\x64\xd0\x4c\x41\xce\x93\xc9\xf2\x31\x47\x2f\x4d\x7d\xb6\x92\x1a\x3a\x4f\xe1\x6c\x77\x96\x3a\x62\xa2\xd4\x82\x87\xf1\xfc\x48\x2d\xa4\xe8\xbb\x69\xb5\x6d\x9a\x01\x45\xc5\x1d\x30\xbe\xcc\x59\x9e\xc6\x92\x3d\x01\xcb\x21\x7e\xdd\x5d\x1f\x6e\x89\x12\x27\x14\xe2\xf6\x6f\x36\x0d\xd7\x23\xea\xc7\xdf\x6f\xed\xdc\x22\xb2\x7d\x72\x0b\x4a\xdb\x2e\x9c\x4b\x79\x9c\xd9\x16\x6f\x71\x0b\x69\xc5\x2d\x1d\x76\x3b\x3f\x7c\x0e\x86\x5d\x69\x7b\x96\x28\x64\x41\xf5\x38\x73\xa9\x5a\x64\x5f\xfe\x3e\xf4\xe5\x85\xd2\xc1\x77\xa0\x8e\xf8\x9b\xa8\xcd\x2d\x8b\xaf\x94\x26\xf9\x05\x1f\x6a\x57\x58\xd9\xc7\x6f\xd8\x43\x7f\x3e\xb2\x06\x3b\xdf\x8e\xbe\x91\xc2\x41\xdb\x5d\xe3\xcc\xa5\xdc\xb5\xc9\x2e\x04\x3c\x11\x5b\xb8\xb8\x22\x61\x4f\x87\x57\xc8\x18\xec\x99\x6e\x7b\x2e\xef\x4e\x2a\xc6\xd2\xbe\x19\x5d\x5a\x81\x2d\x56\xc1\x60\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\xbc\xa6\xc9\x40\xa0\x82\x75\x48\xa5\xb3\x35\x35\xcc\x54\xa0\xbb\xf4\x26\xd6\x13\xdf\x3d\xf4\xc1\x7f\x01\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\x92\x7a\xe2\x6f\xce\xbb\xbf\xb9\xb2\xf8\xdb\x6f\x15\x9e\x62\xdb\xe3\x38\xa1\x3d\xdc\xde\x51\x4a\x1f\xee\xbe\xbf\xb0\x6d\x1d\xf2\xf8\x96\xdc\x3d\x8a\x14\xe4\xb9\x2a\x4f\x64\xba\x94\x53\x5a\xb2\xfc\x95\xb7\x67\x5e\xbb\xf9\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\x73\xe0\xe2\xa7\x9a\x7c\x72\xdf\xcf\x46\x1e\xb2\xa4\xa0\xcc\x8f\xd7\x1f\xe2\x81\x3f\x46\xd3\x78\x7c\x33\x0c\xc7\x28\x1e\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\xff\x0f\x83\xa9\x81\x8d\xe0\xac\x4f\x66\x50\x27\xe2\x9d\x95\x69\x12\x67\x31\xf9\x84\x36\xc8\xe9\x43\x2f\xc0\xea\xa1\x0d\x14\xe1\x2b\x82\x40\x31\x84\x68\x36\x1e\x3b\x16\x8a\xc0\x20\x5f\x26\x52\xbc\x23\x5b\x24\x4f\x3e\x27\xc5\x4a\x6e\xa7\x62\xfb\x43\xd8\x4f\xfc\xe4\x66\x9e\x8e\x5c\xca\x0f\xea\x04\x05\xd9\x42\x99\xd6\x93\x08\x17\xbc\xcb\xfe\x18\x85\xd1\x08\x27\xa1\x12\xc0\x55\x89\xe8\xa0\xe7\x19\x35\x23\x8c\x9a\xd3\x59\x22\xec\x1f\x8f\x31\x0c\xee\x71\xc2\xcf\x60\xe4\x67\x1c\x21\x16\xca\x83\x8a\x41\xc6\xa9\x12\xa1\xa2\x38\x80\x5c\xee\x8a\x2f\x71\x92\x84\x01\x4e\xd1\x21\x55\x88\x84\x38\xa5\x0c\x7c\x7a\x83\xc2\x88\x65\x33\xce\x11\x28\xd1\x82\x9e\xab\xe1\x64\x51\x00\x86\xcc\xe5\x28\xb7\x48\xd4\x40\x32\x51\xfb\x37\x27\x94\x84\x15\xe9\xa6\xc0\x24\x51\xf6\x17\x0b\xf1\x38\xe8\xa2\x17\x90\x29\xeb\x85\x6e\x38\x62\x6f\x93\xfc\x4d\x70\x36\x8a\x83\x42\x1f\x79\xa9\xb4\x1e\x23\xdf\xe6\x78\x86\x90\x19\xce\x90\xa2\xaf\x18\x64\xf3\x79\x75\x06\x31\x9c\xfa\x57\x91\xf9\x45\x62\x24\x44\x58\xc8\xd3\xea\xb9\xcc\x89\x37\x67\x17\x13\x1c\x59\x4c\x87\xc9\x8e\x52\x8c\x05\xca\x99\x0f\x3b\x77\xe5\xe5\xad\xe9\x1f\xac\x08\x30\x33\x29\xee\xfa\x15\x0a\xc7\xd2\xc4\x8e\xd3\x0f\xbc\xc9\x91\x9f\x1e\x5c\x45\x8c\xec\x6f\x2a\x2f\x48\xcd\x17\x55\xe1\xf3\x44\x1e\x61\x13\xe4\xe5\xc9\x8b\xb9\xfd\xa0\xb5\x0a\xa7\xdb\x52\xeb\xff\x49\x67\x53\x22\x6a\x45\x61\xb6\xe2\x13\xe1\x94\x6d\x7d\x7e\x72\x31\x23\xa3\x6b\x1d\x0f\x64\xc9\xa0\x50\x30\x4e\xb9\xc7\x6d\xf2\x22\x45\x39\x47\x0f\xa9\x52\x98\x4f\x3a\x5d\xa5\x26\x04\xb9\x83\xca\x7e\xe0\xd8\x76\x10\x57\x8c\x0f\x71\x82\xa3\x01\x69\x00\xc6\x79\xaa\xaf\x57\x63\x18\x98\x5c\x6c\x03\xe8\xdc\x67\x90\x2d\x35\x86\x8d\xa9\x6e\xc3\x4a\x49\x65\xa6\x49\x55\xde\xb3\x88\x8e\x03\x4c\x20\x5d\xb5\x66\x08\xd4\x4d\x3e\x1f\x79\x06\x9b\x4a\x55\x5c\xc3\x11\x51\x1a\x42\xca\x01\x90\x4a\xf5\xef\xcc\x2b\x79\xc4\x72\xb4\xc1\xd8\x26\xbf\xb3\x98\xcb\x8b\x68\xb9\x62\x8e\x67\x36\x02\x4b\x2e\x8f\x93\x6d\xae\x5c\x1e\x41\x5d\x5a\x23\xfc\x9d\xba\x4e\x9c\x54\xc3\x8b\xdf\x85\x6c\x8a\xdc\xd5\x1d\x73\x85\x0e\x18\x33\x63\x49\x02\x80\xa4\xc0\x84\x3e\x08\x50\x1a\x4f\x30\x4d\x3d\x85\xae\x46\x38\x42\x37\xf1\x2c\x11\x66\xf6\x3e\x11\x67\x29\xf0\x07\x8e\x9d\x7b\xdf\x5d\x50\x77\x74\x2e\xda\xcb\x10\x65\x00\x2b\x2b\xe6\xc8\x88\xa1\xbf\xe3\x76\x37\x17\x8d\x52\x73\xda\x8b\xa7\x44\xd8\x99\xe6\x72\x0f\x93\x77\xee\x21\x4e\x49\xc0\x40\xc3\xa4\xc8\x54\x13\xd0\x44\x3e\xf0\x94\xb2\xd5\x49\xf7\xcf\xb2\xf2\xcb\x1d\xc7\x1d\x1a\x51\x2e\xb1\x45\xff\xac\x6b\x5c\x44\x3c\xe4\x97\x6d\x1f\xfd\x09\x18\x4d\xcc\xa9\x87\xd8\x56\x9d\x17\xd3\x37\x6b\x19\x60\xb5\x70\x8b\x25\xd3\x79\x2a\x17\x3f\x43\x1b\x52\xfb\xea\xa7\x05\x52\x17\x39\x36\xd9\x6d\x74\x15\x47\x2f\x32\x2a\x3f\x73\x77\x47\x29\x78\xe1\x38\x8e\xa7\xc8\xef\xc7\x97\x96\x6d\xb0\xb8\xcb\x2f\x38\xb4\x17\xee\x0e\x03\x17\x15\xad\xca\xfd\x14\x6f\x4b\xe4\xd5\x2a\xb5\x78\xc4\xe1\x04\x7a\x0a\xf6\x2f\x8b\xac\x1b\xdb\xc6\x37\x18\xc7\x11\x7e\x04\x8e\x07\x70\xd1\x46\xbe\x87\xc0\x8b\x12\x3b\x19\x29\x36\x77\x23\x93\x73\x91\xa8\xc2\x11\xe7\xa7\x56\x7b\x32\xfb\x19\xd9\x7a\xbb\x1f\x21\x1f\x3c\x6f\xb5\x58\x84\x85\x91\x85\x8c\x38\xef\xc5\x20\x6c\xe1\x69\x84\xf1\x83\x1a\x0e\x31\x0d\x2f\xa2\x70\x18\x0e\xfc\x28\x63\x01\x25\x43\xda\x7b\x00\x49\xdb\xb1\x1d\x93\x7f\x95\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\x79\x00\xa7\xb5\xeb\x5a\xad\x56\xaf\x35\x6a\x4d\x0f\xd5\xae\x6b\xad\x5a\xbb\xd6\xa9\xad\x9d\x3d\x1a\x60\x0f\x75\x4a\x87\x5e\x61\xe1\xeb\xf8\x8c\x18\x2b\xf6\x8a\x39\x04\xc3\x72\xe5\x0f\xf4\xbf\x5f\xbf\x42\xcc\x5e\x4d\xd4\x18\xa2\x8a\x98\xde\x1f\x36\x2c\x8a\x42\xf9\x0f\xa0\x4a\x46\x43\xfc\x67\x69\x63\x52\x1d\x00\x25\x8f\x31\x8e\x2e\xb2\x11\x35\x3d\x72\x72\x91\xf2\x31\x63\xf2\x85\xb2\x58\xa4\x98\xed\x68\x10\x07\x84\xde\x31\xfd\xa1\x93\x3b\xbc\x2e\x8e\xfd\x29\x08\x00\x47\x83\x95\x5d\x7c\xed\x6e\x73\x5e\x00\x99\x52\xab\x7d\xe1\xe0\x2e\x39\xb1\x96\x88\xec\x62\x89\x6b\x30\x2f\xac\x8b\xa5\x8a\x32\x24\x9f\xb2\xe1\xfa\x42\xd1\x5c\xd8\x54\x38\x63\xb9\xf0\xa9\xfa\xfa\x15\xed\xe2\xeb\xc2\xf0\x2d\x73\x08\x68\xe0\x67\x38\x62\x7b\xbe\x4a\x41\x0e\xe6\xef\x26\x24\xe9\x1e\x36\x1f\xf0\x13\xc6\x0d\x25\xca\x84\x34\xbf\x8b\xde\xeb\x96\xc5\xa5\x0c\x6d\x08\xec\xea\x3c\x7e\x86\x78\xd3\x70\xa7\x34\x83\x92\x3a\x53\xa2\x81\x9d\x17\x0b\x47\x42\x06\xf6\x57\x83\x61\x59\x7c\x15\xb3\x91\x2f\x42\x1d\xe4\x24\xe6\x2e\x1d\xa6\xc7\x39\x8f\x51\x78\x8e\x03\xf8\xb1\xca\x92\x28\xfc\xbc\x8e\xd1\xa9\xde\xd8\x9f\x4c\x11\xbe\x86\x48\x92\xfd\x50\xef\x1c\xbd\x57\x25\x65\xcc\xdb\x06\x7a\x9f\x3a\xb0\x05\x49\x51\x10\xff\x97\x23\x50\x3a\xd4\x27\x22\x69\x84\x61\xab\x45\x7e\x86\x7c\x94\x85\x13\x8b\xc4\x6d\x0b\xc9\x2e\x77\xd7\x9d\x14\x42\x1e\x1c\x52\x14\x6d\x10\xf4\xd8\x2c\x9c\x86\x3c\x2a\x36\xf9\x4f\xa5\xd1\x42\xcb\xa8\x12\x52\x8c\x5f\xa2\xf5\x6a\x55\x44\xcb\x76\x4a\xf1\x14\x8e\xda\xe3\x25\x14\x8a\x70\xdb\x5f\x37\xf2\xa6\xdf\xbc\xe1\x6d\x58\xca\x8b\x46\x4b\x08\xfe\xce\x6d\x49\x1e\x53\xba\xb8\xee\x35\xa6\xee\x28\xf7\x65\xbb\xbf\x81\xcc\xc1\x2e\x93\x31\xd8\xa4\x42\xb1\xd9\x2e\x6d\xa8\x68\xda\x72\xac\xf8\x61\xe4\xf7\xf5\x93\x87\x74\x00\x28\xcb\x4e\x69\x0c\x0e\x22\x04\x2a\x82\x61\x98\xdd\x57\x14\xcc\x17\xa7\x58\x5d\x0e\x26\x45\x3e\x97\x0d\xdd\x6b\x61\x4d\xa6\x1c\x65\x8b\x8b\xe4\x64\x32\x76\x86\x61\x11\xd5\x4e\x05\x0c\x1e\x67\x7e\x03\x96\x0e\xfd\x03\xd2\x6f\x36\x08\xe9\xa7\x0a\x5f\xb0\x10\xbc\x22\x4a\x6d\xa0\x7d\x3f\x1b\xad\x0c\x70\x38\xce\x6b\xae\xa2\x05\x22\x12\xd9\xcf\xbf\xa5\x76\x1e\x87\x39\x92\x71\xfc\xbd\xab\xdd\x27\x3b\xee\xca\xb4\x60\x9c\x77\x55\x5a\x98\x77\xce\x95\xc1\xc2\x49\x8d\xe2\x2a\x47\x3f\x37\x4f\xce\x2b\x26\x8d\x30\xf3\xfb\x9a\xd3\xa4\x8e\xd4\x5b\x7c\x0a\x24\xb1\x61\x18\x8e\xc7\x3c\xec\x2c\x73\x93\x80\xf3\xd6\x7c\xa1\x84\x1f\xe6\x22\xdb\xa1\x57\x06\xe5\x74\xf1\x29\x35\xcb\x0c\x52\x29\x42\x79\x28\xe3\xb3\x12\x47\x30\xe6\x0a\x52\x77\x9f\xb4\x68\x09\x99\x4c\x22\xfb\x11\x4b\x66\x0f\xe6\x81\x8a\x7c\x4d\xd4\x1b\xf2\xc9\xf9\x95\x3b\xca\xfc\xf9\x15\xda\x20\xff\x3a\x12\xa8\x4d\xce\xbf\x90\x6d\xe6\xba\xe9\x07\xb8\xb3\xde\xd7\xc3\xaf\x8b\x62\x7e\xfa\x19\xc9\x9c\xa3\xe0\x9e\xa0\xc4\xdd\x1d\x6d\xb5\x52\xbb\x7e\x55\xeb\xbc\x42\x2f\x49\x17\xbe\xc0\x9e\xbe\xb3\xb3\xb3\x53\x45\x4b\xf4\xc5\xcf\x3f\xa3\xda\x75\xbd\x06\xdb\x3d\x41\xc0\xb1\xdd\xd3\x2e\x56\x6a\xd7\xad\x4e\xbb\x46\x81\x5d\xe9\xc0\xae\xca\x02\x83\xe1\xc5\xe9\x0c\x3c\x7d\x2a\x80\xc6\x9b\x37\xb4\x26\x5a\x42\x30\xd2\x85\xf5\x59\xdd\xd5\x0d\xa8\xc3\xfe\x8a\xcb\x2e\x6d\xa0\xda\x4a\xdb\x59\x06\xc6\x94\x15\x7d\x49\xed\x6d\x38\xb5\x55\xd1\xcf\x68\xa5\x8d\xfe\x03\xd5\x51\x17\x2d\xd7\xcb\x88\x28\x06\xe7\x50\xc5\x0d\x0f\x25\x03\x7f\x30\xc2\x2c\xbb\xce\x7c\x81\x83\xd4\x3c\x27\xf4\x98\x54\x2a\xb4\x2a\x39\x2a\x29\x48\x92\xdd\x44\x1a\x0c\xfb\x15\x13\xad\xba\x81\xce\x93\x0a\x2d\x0f\x04\xb9\xd6\x5f\xb3\xf4\xe9\x2a\xcf\xe1\x53\x11\xe5\x73\xf8\xe8\x2b\xaa\x95\x0c\x6b\x1e\xe1\x2b\xc9\xd9\x09\x6e\x1d\x99\x02\x24\xe2\xe9\x7b\x9e\x69\x23\x69\x77\x3e\x65\x47\xfb\x79\x86\x34\x38\x1a\x80\x21\x0d\xfd\xaf\xdd\x90\x66\x17\x5f\x9b\x9a\x00\x1b\x38\x52\x70\x83\x02\x5d\xa1\xbf\xcb\xc5\xdf\xd4\xd5\x17\x23\x7c\x5d\x5a\x85\x51\xe2\xe4\xb9\x60\x54\xcd\x52\xad\x3f\x14\x23\x1f\xe1\x6b\x33\x84\x26\x1b\x3f\xe9\x68\x3f\x3f\x91\x90\x35\x70\xe6\x5d\x8f\xa9\x57\xa5\x4f\x9e\xe9\xa2\xc7\x48\x3a\xeb\x26\xa0\x11\xbe\xee\x8d\xfc\xa4\x74\x9e\xad\x74\xee\x81\x0e\x72\xa4\x85\xf4\x20\x77\x75\xcf\x43\x1c\xc7\x8e\xad\x71\x00\x4b\x80\xb4\xaa\xb9\xda\xa7\xde\xa9\xda\xf8\x9d\xad\x2a\x69\xa7\x36\x2c\xae\xeb\x60\x10\x02\xdc\x1f\x71\x18\x55\x5e\xbc\xb8\x43\xc4\x4d\x89\xc2\xe9\x7a\x5b\x44\xd3\xc3\x57\x0a\x25\xdc\xf2\x0b\xc6\x21\x3c\xfd\xf5\x52\x13\x5f\x6c\xd4\x66\x5b\xac\xc7\xf2\x91\x32\x69\x95\xc5\x12\xa5\xd0\x3a\x1f\xf8\xd1\x85\x3e\xb2\xa3\xcc\x22\xab\xe6\x6a\x91\xd4\x74\x72\xa3\x6c\x0b\x6d\x14\xe4\xc7\xa4\xab\xa5\x09\x9a\x09\xe8\xf4\x5e\x94\xb1\xce\xae\xa4\xb3\x7e\x9a\x25\x95\xd0\x43\x8d\xaa\x07\x49\xf8\x72\x95\x05\x59\x51\xeb\x55\x9b\x03\xee\xc2\x7b\x9e\x32\x4c\xab\xa8\x51\xd6\x7d\xf6\x83\x9f\x85\x51\xbd\xdc\xa6\xc5\xca\xf2\x7d\x4b\x3c\xde\x6d\xeb\x62\xd5\xff\xba\xdd\xab\x2c\x02\x0f\xb5\xa6\xc6\xd0\x9e\x7d\x0f\xa3\xb8\xfc\x8f\xda\xc6\xe8\x70\x7c\xc7\x3b\x99\x84\x20\xdd\x91\xe8\xd4\xad\x0c\x93\x78\x42\xde\xf6\xe2\x00\xc3\x26\x55\x76\x43\x92\x01\xde\x63\x4f\x52\xe8\xf6\xee\xdb\x92\x20\xc7\x85\x16\xc3\x77\xbd\x39\xb1\x55\x44\xf7\x27\x79\xb9\x95\xdf\xa2\x44\xad\xc5\x76\x29\x51\x4d\x6c\x54\xe2\xcd\x63\xef\x55\x5a\xd3\xf3\x72\x39\x87\x92\x16\x3d\xef\xed\xca\x80\x11\xf4\x66\x56\x09\xf9\x9a\xd0\xb7\x2a\xbb\x6e\x71\xe1\xad\x4a\x43\xb8\xec\x4e\xf5\xe9\x64\x67\x79\xbd\xdc\x46\xf5\x29\x1b\xae\x8b\x6d\x8a\x3d\xdc\x6d\x93\xa2\x8d\xfe\x75\x7b\x54\xc9\xf6\x1f\x6a\x65\xcd\xb2\xe1\xba\x7d\x83\x22\xa3\xf8\x98\xdb\x53\x96\xdc\x14\x18\x18\x05\x98\x1c\xd1\x3f\x1d\xed\xf5\xb8\xa7\x53\x05\xa7\x03\x7f\x8a\x2b\x05\x1b\xa7\xc9\x96\xd1\xc0\xcf\x06\x23\x54\x31\xd3\x47\x03\x0a\xa3\x24\xbe\x02\xba\x85\x8c\x2b\x95\x17\xfb\xfe\x78\x18\x27\x13\x1c\xb0\x69\x08\xfc\xcc\x37\x53\xd0\x2d\xce\xc0\xe5\x49\xbd\x3b\xff\x66\x73\xb5\x08\x99\x7c\xd7\xcc\x1b\x28\x8c\xb2\xee\x9c\x0c\xcb\x33\x6e\x56\xc7\x65\x0c\xa0\x6c\x0d\xb3\x88\x51\x0f\xb5\x10\x50\xe8\x8a\xc3\xa9\x96\x0e\x40\x23\x52\xf0\x42\x2e\x4c\x1c\xb0\x6c\x66\x92\x17\xba\x33\x13\xaf\x64\x27\x7b\x23\xa5\x44\x9b\xcc\xd2\x0c\xf5\x31\x0a\xc9\x88\x4e\x70\x94\xd1\x3c\x6b\x3e\x5c\xaf\x27\x38\x13\x1e\x0b\xa5\x72\xfb\x6a\x79\x3a\x55\xe5\x3e\xcd\x71\x48\x5d\xab\xf2\x04\xf1\x9f\xf1\x34\x43\xb3\x68\xca\x93\x06\xaa\xd9\x41\x25\x9b\x96\x9a\x85\xfb\xbe\x65\xe3\x00\x99\x06\x37\xc5\x28\x08\x2f\x31\xd7\xe7\x92\x66\x70\x90\xdd\x95\x59\xf3\x68\x23\xfd\x82\x25\xd1\x66\x49\x4c\xb3\x18\x85\x59\xca\xbd\x62\x10\xa1\xe0\xfb\xde\x31\xf5\xad\xc8\xd3\x84\xb8\xee\x4b\xa6\x52\x59\x77\x99\x79\x1f\x02\x2b\x65\x9b\xcd\x00\x64\xe0\x64\x9e\x8a\xda\xce\xaa\x33\x25\x5a\x3e\xda\xf2\x33\x9f\x0b\xeb\xb5\xb2\x92\xe6\x66\x10\xa4\xd0\x06\xcf\x0b\xee\x18\x69\x46\x0b\xe5\x37\x45\x11\x64\xc1\xc8\x3c\xce\x8c\x5d\x10\x5d\xf3\xcc\x09\x80\xf2\x4b\xea\x53\xe2\x4b\x16\x94\xd4\x9e\x18\x38\xde\xe3\x4c\xe6\x39\x45\xa7\xf2\xc2\xe4\xf7\xa5\xea\xcd\xdf\x1b\x59\xc9\x32\xc9\xcc\x4d\xf7\xfa\x3c\x1d\x9d\x1c\x50\x54\x1a\x20\x16\x4c\x54\x05\x25\xfb\x38\x03\x19\xcd\x89\x13\xc9\x68\x4d\x62\xca\x80\xe1\xfc\x48\x69\x9b\xd0\x35\x17\xf9\x72\x53\x22\x1b\x30\x83\x68\x97\x36\xd4\x24\xe9\x65\x29\x98\xe7\x3a\x4d\x91\x7f\xe9\x87\x63\x88\xd8\x45\xf9\x02\x30\x3b\x37\xd5\x9c\x48\xce\x2a\x61\x74\x19\x7f\xc6\xa9\x9e\x64\xb8\xc2\x92\x03\x7b\xe8\x6a\x14\x0e\x46\x56\x56\xdd\xbf\x29\x60\xd5\x66\xab\x7c\xa1\xf4\xe3\x78\x8c\xfd\xe8\x16\x05\xf1\xce\x78\x96\x8e\xd0\xaf\x23\x9c\xd1\x78\x26\x3c\x17\x2d\xb8\x6b\x4d\xfd\x04\x18\x05\x7b\x95\x73\x6d\xc1\xae\xef\x10\x0e\x44\x70\x7a\x18\xf1\xfb\x6f\xf3\x02\xe0\x16\x25\x24\xd7\x9a\xe1\xa9\x72\x5d\x71\x39\x16\x04\x63\xcf\x14\xac\xc6\x5a\xa5\x45\x95\xc5\x47\x07\x7c\x41\x9d\x09\x5b\x22\x39\x71\x5b\xb4\x25\xe4\x35\x37\x4e\x83\x91\x75\xa9\x55\xc8\x47\xc9\xd0\xcc\x45\xf7\xbc\x78\x2e\x2b\x6c\x68\x29\x99\x8b\x0a\x73\xe8\x79\x6d\x7b\x44\xbf\x5e\x3c\x8b\x32\x4e\x5f\x16\x66\x42\x80\x46\x34\x91\xf0\x11\xc4\x2d\xde\x50\xf1\x5f\xd5\x9a\x7c\x6d\xf2\x22\xd7\x90\x33\x0c\x8e\xe2\x59\x14\xa0\xd9\x94\x3a\x14\x0e\xc6\xb3\x00\x6b\x74\x6f\x56\xd3\x30\xca\x8d\x5c\xe4\x0f\xe5\x63\xdb\x0a\x2c\x82\xf8\x2a\x92\xf1\x88\xa3\xf1\x0d\x1a\xce\xc4\xa2\xb4\x44\xd2\x5f\x5d\x45\x63\x9c\x52\xa7\x4a\xbb\xac\x05\x7c\x23\xc1\x13\x3f\x8c\x54\xe1\xaa\x5c\xbf\x26\xfe\x75\x45\xe9\x17\x5c\x9c\xa2\x65\x5b\x66\x76\x6f\xfe\x95\xaa\x98\x73\xaa\x79\x70\x4d\x39\x50\x32\xc7\x43\x69\xfd\x25\x92\x08\xd0\x45\x4f\x40\x1b\x4e\x72\x22\x5f\xd5\x3e\x86\x51\x45\x6e\xf2\x25\x6a\x79\x0a\x9d\xd9\xcc\x27\x79\x06\x6f\x1b\x91\x10\xba\x93\x00\x16\xbb\x6d\x51\x3e\x4f\xd5\x2c\xec\xf7\x1b\x79\x04\xc4\xdb\x25\x69\x3d\x39\x8d\x26\x08\x66\x38\x21\xa7\x49\xb1\x31\x2c\xe7\x07\x04\x70\x86\xb4\x57\x64\xdc\x45\xdd\x83\x04\x57\xb1\xe5\xaa\x77\xcd\x31\x52\x52\x60\xe5\x0c\x1f\xa6\xdc\x2c\xaa\x70\x5f\x99\x85\xe9\xc9\xb0\xe4\x11\xb5\xa0\xa1\x70\x32\xb4\xbc\x21\xcf\xf4\x7c\xaa\xe4\xb1\x45\x8b\xb0\x75\x2b\x9c\x54\xfc\x3d\xb9\xe9\xfb\x1a\xbb\x95\xce\x42\x59\xe8\xe4\x75\x4f\x2b\x37\xc7\x6e\xf8\x17\x99\xbc\x9d\x1b\x1b\x62\x8e\x89\x75\xc6\x0a\x2d\xde\x54\x1e\x26\x4e\x9a\x8e\x4c\xf4\xfc\x0c\x3e\xf2\x53\xc8\x90\xeb\x3c\x71\xcf\x4d\x45\x9e\xb3\x6b\xd9\x07\x8a\x4e\x3a\x83\x4e\xc3\xae\xe1\x14\xc5\x91\x74\x14\xae\x77\x50\xa5\x5d\x6f\x80\x25\x6b\xd5\x72\x2c\xde\xa5\x95\xf9\x31\x58\x3c\xda\xcf\xc3\x0f\x12\xf5\xb5\x28\x03\x59\x61\xc0\xd4\x22\x57\x33\x3a\x08\x0b\xe4\x24\xbf\x6b\x74\x3b\xd2\x10\xa2\x21\x92\xe7\x05\xb9\x2b\x6d\x43\x22\xe6\x40\x09\xdd\x76\xbc\xbb\xd9\x68\x77\xec\x4e\x62\x45\xa9\xae\xef\x1c\x61\x8d\xc7\x56\x2b\x1f\x66\xed\x18\x8b\xf0\x1e\x6e\x0d\x81\xa9\x86\x98\x63\x89\x9d\x6b\x52\xf8\xc2\x79\x78\x95\x09\xa3\x97\x87\x50\x91\x00\xc2\xb2\x8a\x47\x2d\xe1\x58\x49\x00\x5a\x61\x5e\xa6\xd4\xa0\xef\xcd\x6c\x38\x2c\x1b\x33\xdf\x90\x8f\x16\x1b\xeb\x4f\xd3\x00\x58\x86\x3c\xd8\x34\x2d\x7f\xf9\x8c\x7d\xce\x08\xc2\x14\xb8\x1e\x47\xb8\xb4\x0b\x11\x65\x45\xcc\x7f\x68\xee\xf2\x5e\x60\xce\x67\x80\x57\xe5\x05\x43\xca\xa6\x4b\x51\x4b\xce\x57\x9d\xd0\x82\x32\xa1\x28\x63\xe0\x58\x8f\x0e\x8d\x04\x53\xd8\xa8\x10\x2c\xe4\xc1\xc6\x97\x08\xe9\x04\x5f\x1b\x28\xe9\x1c\x6b\x8a\xbf\x0f\xe6\x3b\xb1\xc3\x92\xdc\xa4\x02\x17\x27\x83\x44\x1f\x63\x40\xd9\xcf\x68\xbe\x78\x56\x33\x8f\x19\x8a\xc2\x14\xe1\xe1\x10\x0f\xb2\xf0\x12\x8f\x6f\x90\x8f\x02\x9c\x66\xc9\x0c\x9e\x3d\x90\xd3\x97\xe3\x68\x80\x4b\x45\x19\x2d\x49\xa1\x4a\xa2\x07\x40\x29\x0f\xc8\x0d\x25\x16\xd7\x5c\x90\x41\x78\xa0\x9d\x01\x6d\x70\x72\x14\xc9\x84\x1c\x6a\x09\x47\xe9\x22\x42\x2f\xa8\x36\x9f\xea\x79\xd1\x85\xe8\x7e\xc7\x32\xbe\xe6\x81\xa8\x18\x0c\x9a\xb7\x56\xe6\x09\xf0\x0b\x70\x56\x69\x84\x38\x93\xdd\x91\xe6\xc1\xba\x78\x48\x79\xd7\xe2\x91\x92\xdf\xb5\xeb\x8d\xd5\x66\xa3\x9c\x98\x9f\x32\x8d\x8f\x12\xff\xde\x67\x93\xf6\x42\x04\x4e\x0a\xa3\x0c\x27\x43\xc9\x5a\x18\x39\x57\x05\xe7\xaf\xac\xeb\x9c\x6a\xe9\x76\xcb\xe2\x23\xfa\x68\x84\xc7\x53\x9c\x10\xf1\xa7\xc4\x22\xd8\x61\xb8\x31\xdf\x60\x1d\xe5\x6f\x70\x8f\x47\x65\x26\xdd\xa9\x82\x76\x75\xe5\x9c\xf6\x6a\x17\xba\x54\xb1\x09\x5b\x6e\xfd\x9c\x5c\x55\x31\x1e\x04\xd0\xae\xfb\x3d\x63\x5d\xd8\x03\xe0\x22\xf5\xbc\xc8\x56\x22\x1c\x16\xd5\x2c\x62\x79\x86\x4b\x95\xc2\x17\x3f\x36\x5a\xe9\x89\xb0\xe4\xdd\xfd\xcd\xde\xc3\xd3\x13\x11\xa1\x79\x50\x0a\xd2\x02\xa3\xab\xbf\x05\x4d\xed\x4e\xfc\x41\x29\xba\x9a\xf8\x83\xfb\xd0\x96\xa8\x7e\x2f\xfa\xfa\x8c\xed\x2a\x24\x89\xbe\x7a\xe7\x80\x16\x99\x07\x4a\x64\xb4\x11\x5a\x77\x31\x62\x2b\x3c\xfe\x0a\x4d\xd2\x1c\x1f\x06\x82\x0d\x38\x31\xb0\x1f\xb9\x17\x03\xcf\xd4\x02\x21\x7d\xf7\xfd\x6c\x44\xc3\xfa\x3e\xe3\xef\xd9\x30\xbf\xce\x23\xfd\xde\x9e\x79\xed\xd6\xf7\x1a\xde\x97\x21\x53\xe1\xe1\x88\xab\x0f\x1e\xef\x97\x43\x5e\x34\xee\xaf\xc0\x50\x8e\xff\xeb\x0a\xfa\x2b\xbe\x43\xf0\x5f\x5b\x00\x5d\xf3\x8a\x82\x47\x8d\xcd\xa7\x4c\x22\x00\x29\x1a\xac\xf4\xbe\x20\x3c\x8d\x52\x5b\x72\x81\x71\x85\x91\xed\xb4\xca\x99\x68\xb1\xb2\xdc\x48\x4b\x3c\xde\xcd\x4c\x8b\x55\xff\xeb\xec\xb4\xca\x22\xf0\x50\x9c\xb2\x0f\xed\xd9\x4d\xb5\x28\x2e\xff\x00\x5b\x62\xa3\xfc\xc4\x9f\x0a\xe1\x70\xe2\x4f\x17\x8f\xbd\x60\x71\x11\x37\x41\xb8\xac\x32\xe9\x98\xdf\xd5\x60\x19\x2d\x6d\xa0\xa6\xdb\x66\xf9\x26\xc3\x75\x8b\xd1\x32\xfd\x73\x99\x2e\xd3\x3f\xa7\x01\x33\x07\xdc\xc8\x01\x57\x42\xb4\x84\xea\x55\x8b\x4d\x34\xff\x52\xc6\x32\x9a\x03\x6e\x6a\x80\x1b\x4e\xc0\x0d\x2b\x60\x3b\xe4\x2c\x09\xa7\x63\xb8\x7a\xa9\xd0\x61\x79\xf3\x06\xfc\x26\xbe\xd2\xe7\x06\x79\x5e\x27\x8f\x80\x82\x0d\x8a\x98\x8a\x3f\xe8\x54\x54\xfe\x40\x6f\x48\xeb\x3f\xfd\x84\x00\x9b\x3f\xd0\x4b\x54\x5b\x59\x6b\x4b\x33\x54\x7d\x8d\xfe\x28\x08\x77\x21\xcd\x3d\xb5\x05\x9f\xf8\x53\xb0\x99\xdd\xcc\x2a\x15\x8e\x30\x74\xba\x83\x5e\xa2\x4a\x13\x2d\xa3\x3f\xaa\xac\xa7\xcd\xa1\xd5\xdb\xc9\x88\xcf\x60\x2a\x2e\x82\x80\xa7\xfb\x36\xa9\x91\x7d\x20\x28\xa1\x0d\x24\xa1\xd3\x31\x9c\x49\x20\xb6\x5e\x5e\xdc\x6e\x1c\x3c\x0a\xc7\x18\x55\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\xbc\xb9\xa5\xa8\xb5\xe0\xe6\x20\xea\x09\x43\x79\xf1\x46\x18\xca\xcf\xe7\xfb\x46\x89\x04\x5f\xe2\x24\xc5\xfb\x52\xc1\xfc\x95\x2d\xae\xd9\x0f\xf9\x67\x27\x75\x17\x02\xb5\x6d\x01\xfc\x4f\xe7\x3f\x84\xfd\x90\x15\xca\x3a\x58\xc8\x69\xd4\x86\x4f\xf9\xc2\x66\xb6\xf9\x7f\x54\xcf\xd0\x06\xfa\xa3\x5c\xac\x4e\x0b\x4b\xd9\xbb\x88\xe2\x04\x7f\x33\xae\x22\x81\xdc\x8b\x02\xf0\x73\xce\xa7\x3b\x24\x6f\x0e\x86\xf3\x78\x86\xd4\x0e\x85\xf1\xc3\xc6\x06\x5a\xae\xcf\xe1\x49\x32\x85\xc9\xb5\xef\xc4\x88\xad\x22\x41\x22\xd2\x5e\xa6\xf8\x43\x1c\x4f\xf3\x25\xe1\xe9\x38\x78\xd2\x8c\x2a\x22\x87\x76\xe3\xe9\x4f\xbb\xe8\xc5\xe6\xdb\xde\xd6\xf6\xce\xbb\xdd\xbd\xff\x7a\xff\x61\xff\xe3\xc1\xe1\xff\x3e\x3a\x3e\xf9\xf4\xcb\xaf\xbf\xfd\xfb\xff\xf8\xfd\x41\x80\x87\x17\xa3\xf0\x8f\xcf\xe3\x49\x14\x4f\xff\x3b\x49\xb3\xd9\xe5\xd5\xf5\xcd\x97\x5a\xbd\xd1\x6c\xb5\x3b\x6b\xeb\xaf\x96\x56\x37\x58\x84\x5b\x71\xb4\x13\x8b\x76\x61\x54\xf3\x21\x76\x78\xa5\xe4\x96\x1b\x8a\x85\xa9\x4d\x14\xd2\xda\xb1\xb9\xa9\x90\x99\x0e\x1d\xfb\x0d\x73\xec\x4a\x89\x90\x24\x2d\x8f\x9c\x9a\x64\x07\x16\xb4\x8c\xea\xd5\x33\xf0\x5e\xc9\x05\xa6\x86\x49\x5c\x1c\x68\xa3\x0c\xd0\xea\x19\xdf\xe0\x65\x31\xcc\x02\x95\x0a\x44\x91\x12\xb9\xe7\x2b\x11\x66\x00\xfd\xaf\xb4\x45\xd9\xb7\x26\x2a\x0e\xde\x83\xd8\x10\x2f\x2d\x29\x1f\x04\xd9\x8a\x1f\x8c\x22\x8d\xd8\x92\xd6\xb0\x08\xb7\x79\xee\x1e\xfd\x90\x2f\xed\x11\xaf\x9d\x99\x7d\xda\x4f\x47\xff\xa7\xa3\xbf\x38\xfa\x7f\x3a\xd9\x59\xae\x77\xd0\xdb\xed\xd2\x0e\x5a\xf5\xce\xdb\x6d\xd9\x47\xab\xde\x51\x9f\xe0\xeb\xdd\x9d\xb6\x28\x32\x7f\xad\xe3\x56\x49\x1c\x1e\xd0\x79\xab\xde\x71\x7a\x6f\xd5\x3b\xff\x00\x8d\x40\xf9\xc3\x3a\x0c\xc6\x7d\xce\xea\x76\x7f\x7f\xb0\x8c\x8a\x03\x7c\x18\x87\x51\xe6\x72\x32\xae\x77\x1c\x4e\xc6\xd6\xc3\x74\x8e\xa9\xdb\xcb\x58\x34\x59\xd6\xd5\x58\x02\x7a\x8f\x13\x94\x4e\xc4\xf7\x72\x56\x03\xda\x5c\x74\x6d\x7c\xd7\xc7\x28\xba\xaa\x84\xcb\x1a\x5f\x7c\x0b\xf9\xac\x41\xa5\xc5\x7c\x8d\x79\x2d\x21\xdf\xf2\x17\x8f\xed\x69\xac\x36\x5c\xce\xd1\xb8\x0e\xb2\x8f\xc0\x50\x75\x33\x26\x22\x50\xbe\x58\x1a\x64\xb1\x68\x41\xd8\xdc\x14\xee\x92\x72\xb4\xd1\x79\x59\x3e\x14\x06\x23\xcb\x0f\x25\xf6\x30\x69\x9f\xfa\x70\xef\x7d\xea\xc3\x77\xb0\x4f\x95\xc1\xe1\xa1\xf7\x29\xeb\x72\xfa\xb0\xfd\xb4\x4d\x89\xbf\x07\xdb\xa6\xd2\x2b\x7f\xba\x1d\x05\xa1\x1f\x55\x16\xdd\xb1\x6c\x47\xf2\xef\x7f\xcb\xfa\xf0\x38\x5b\x56\x99\x65\xf2\xfd\x6f\x59\x1f\xb6\xb5\x4d\xeb\x69\xc7\x32\x76\x2c\x69\xc5\x2c\xb4\x79\x7d\xd3\xdd\x4b\xcc\x8b\x84\x2d\x01\xa4\xf4\x91\x47\xc3\x87\x2f\xec\xee\x84\x2e\xee\x5a\x8d\xfc\x3f\x5c\xac\xd0\x8f\xa4\xfb\xec\x2b\xfd\x96\x2f\xff\x79\xea\x02\x20\x2c\xb7\xb6\xa0\x73\x2f\x6d\x01\xcb\x51\xfb\x2d\x95\x06\x1e\x92\x5e\xa5\x23\xbf\xae\xbd\x1a\x4d\xfc\xc1\x23\xaa\x16\x3c\xc4\x9b\x85\x5f\xd0\xda\x3f\x41\xdd\x60\xe4\x8b\xbd\x83\x2a\x42\x31\x62\x91\xbe\xec\x6f\xb5\xa1\x26\x98\xdc\xec\x6f\xb5\x6d\x32\x1e\x98\x38\x7f\xc6\x37\x34\x0b\x36\xb5\x83\x15\x7d\x05\xe7\x5f\x3f\xca\x78\x12\xef\x38\x99\x50\x1b\xed\xed\x5f\x0e\xcf\x61\xd3\x3d\x89\xdf\xe3\x5c\x18\x44\x57\x57\x57\x2b\xf1\x14\x47\x69\x3a\x5e\x89\x93\x8b\xd5\x20\x1e\xa4\xab\x90\x84\x3b\x5e\xd5\xea\x8c\xb2\xc9\xd8\xa2\x08\xd9\xbe\x9c\xbe\xdf\xda\xc9\xd1\x16\xcf\x25\x83\x21\xcc\xf7\x01\xd1\xf6\x38\xc3\xfb\x85\xa5\x3c\x87\x3d\x8a\x0c\x4c\x4a\x1e\xc2\x88\xbb\xbd\x48\xe1\x9e\x73\x57\x97\x16\xaa\xd4\x1b\xeb\x8a\xa7\x8b\x01\xdf\x61\xa4\x26\x87\xc5\xd0\x13\xa4\xec\x6f\xb5\xe7\x61\x1b\x66\xcc\x16\x59\x0f\x52\x2d\x7d\xc8\x62\x34\xa5\x56\xa7\xb2\x77\x8e\x63\x87\x33\xfc\x62\xb4\xdd\x81\x0d\x4f\x17\xd5\x1b\xeb\x60\x42\xaa\x7c\xa5\x9d\x03\xcc\xb5\x2f\x39\x3e\x4a\xdb\xb7\x77\x76\xbb\x71\x10\xed\x63\xfb\xe1\x60\xa9\xd1\x07\x30\xb3\xfe\x1c\x0c\x0d\xef\x1b\x4a\xf3\x73\x52\x34\xcd\xaf\xf8\x67\x3e\x57\xeb\x5a\x3e\xbf\xbb\x82\xf1\xd4\x69\xac\xd5\x6a\x3a\xe0\x05\xbd\x83\xe6\xfa\xfd\x94\x93\x77\xb7\x20\x85\x3f\xa1\x11\x42\x15\x90\x08\xdb\x87\x0c\xac\x64\xd1\xde\xc5\x4a\x9f\xd7\xa5\xb1\x00\x6c\x80\x0a\x2a\xa7\xfe\x38\x43\x9b\xf0\x9f\xc5\xc5\x62\xa0\x2e\x4a\xde\x0f\x41\x5e\x98\x6c\x1e\x9f\x83\xe1\x0a\x75\x8b\xc0\x15\xde\x19\x0f\xf0\x2b\xc8\x5b\x03\xc5\x95\xfc\x8e\x6a\xcd\x85\x04\x5e\x75\x8a\x2d\xe2\x2d\x59\xe9\x8c\x7b\x98\xb5\x85\x97\x1a\x21\x0f\x66\xa2\x9c\xaf\x0e\x2b\x2c\x97\x5b\x18\x84\x16\xa0\x43\xfc\x1e\xc6\xc6\x96\x12\x6d\x91\x33\x72\x0e\x4c\xf8\x04\x8b\x37\xce\xe3\x32\xdf\x63\x68\x8f\xd8\x93\xa5\x9c\xc4\xc4\x69\xd1\xfc\x85\x05\xcb\x77\x6c\x63\x22\xe0\xd5\x8f\xcc\x98\x45\xc3\x95\x1b\xb4\xbc\xe1\xf8\x58\x8f\x02\x44\x8c\x03\xcf\x01\xe7\x05\xb3\xea\xb2\x44\xcb\xce\xbf\x56\x46\x72\x30\x86\xdc\x09\x84\x41\xe1\xc4\x26\x19\x05\x1b\xf4\xaa\x36\x2f\xfc\xe9\xcc\x12\x84\x26\xc4\xc0\x99\x9f\x95\x83\x52\x9d\x1e\x94\xa4\x81\x2e\x4c\xfb\xa3\x61\x2f\x90\x75\x8e\x82\x0d\x63\xcb\x50\x99\xef\x24\xb2\x62\x31\x63\xac\x6d\x68\xa3\x2c\xd5\x92\x74\x34\x9c\xfe\x2c\xd1\x2e\x44\x80\x39\x5e\xaf\xac\xcd\x75\x29\x1e\x2c\xfb\x1d\xdf\x89\xf7\x2e\xc8\x77\x1f\xd0\xfb\xd6\xe2\x57\x26\xf5\xa6\x3c\x37\x97\x2a\x29\xda\x0d\xe9\xbd\xca\xdd\xf3\x0f\x48\xe1\xea\x62\xd3\xa6\xfb\xb5\x8b\xb3\x2f\x56\xcd\x43\x0e\xb1\xe1\x3e\x60\x0a\xc5\x06\xa1\x42\xce\x65\x7d\xd7\x9e\x63\xba\xb0\xb0\x61\x57\x25\x16\x70\x5c\x29\xde\xef\x6e\x5f\x17\x1c\xdf\x29\x34\xfb\xd9\xdd\xe3\x87\xcf\x6e\x7b\xdd\xe3\x47\xd2\xee\xda\x1a\x39\xd3\xaf\xfd\xad\xcf\xf4\x83\x70\x3a\xc2\xc9\xf2\x23\x9b\x08\xc0\xe9\x5d\x6e\xea\xaf\x39\xc4\x9b\x99\x3b\x1f\xe4\x34\xdf\x83\x8e\x1d\x12\x8e\x93\x8a\x43\xbb\xfc\xd2\x6d\x42\x20\xde\x6b\x99\x30\x94\x1a\xe4\x0c\xe7\x67\x50\x89\xfe\xe4\x8c\x98\x55\xdc\x81\x97\x19\x8b\xaa\x40\x8b\x2c\x90\x4e\x83\x9c\x6e\xe8\xdc\x64\xf8\x3a\x23\xa7\x48\x9f\x3d\xa3\x29\xed\x13\xf3\xcd\xe2\xa9\x36\xfc\x00\x0f\xc2\x89\x3f\x1e\xdf\xb0\x34\xa0\x41\xe9\x9b\x1b\x79\x54\x6e\x59\x2b\x6c\xe0\x4e\x04\x1a\x6a\xb3\x8b\x27\xe3\xb8\x0b\x7e\x8f\x9a\x9e\x23\x9f\x12\xe9\x56\x47\xee\xfc\x62\x17\x3b\x4a\x4d\x87\xa3\x96\x5c\x66\x25\x9f\xdd\x3c\x81\xc4\x2e\xbe\xbe\x63\x26\x08\xcb\xf0\x4a\xe4\x23\xdf\x37\x2c\x38\x9d\xda\xcd\x43\x18\x4d\x67\xd9\x7d\xe6\x94\x93\x87\x4a\x74\x77\xa0\xb3\x87\x22\x8e\x81\xc6\x28\x2c\xf4\x71\xe7\xa4\x12\x30\x5a\xf6\x10\x36\xf9\xe4\x6c\xa0\xbc\x0d\x5a\xe1\xb5\x95\x7a\x7a\x0a\xf5\x70\x8d\x40\x0e\xa8\x2b\x03\xbd\xb5\xeb\xe6\xdd\x3b\x6d\xde\x5d\x6d\xb7\x95\x36\x88\x6e\xbb\xe1\x69\xca\xf3\xf5\x27\x53\xbb\x7f\xba\xee\xdb\xb5\x3b\x1a\x91\xcc\x8b\x34\xe1\xe6\x21\x05\x1c\x80\x85\xc6\xd5\x9a\x88\x8a\x94\xd8\x90\x1d\x55\x1f\x26\x21\x3d\xb8\xbc\xce\xe5\x78\xa5\x95\xc4\x25\x55\x51\x44\x56\x07\xe7\x65\x3c\x48\x70\xf6\x40\x4a\x25\x22\xff\xee\xda\x03\x07\x41\x2f\x19\x9b\xb0\x79\x22\x53\x47\xdf\xb2\x1a\x43\xd9\x39\xd8\x11\x20\xd8\xaa\x33\x12\xfa\x22\xea\xa3\x20\x1e\x75\x0f\xf7\x02\x6f\xb7\x87\x8c\x2f\x0b\x07\xa6\x39\xe1\x65\xe9\xa1\x4a\x8a\x2e\xab\x8f\x93\xdd\x10\xbf\x40\x31\x45\x3b\xfa\x56\x8a\x8b\xc9\xba\x5e\x14\x19\x53\xab\xc4\xf5\x05\x3a\x2c\x7b\x94\xcc\xcd\xf1\x38\xbe\x42\x7e\xd2\x0f\xb3\xc4\x4f\x6e\x10\x53\x2f\x7d\xc6\x37\x96\xb8\x83\x9f\x65\x8d\xc4\xcf\xd6\x86\x0b\x06\x4a\x57\xb7\x94\x1b\xad\x39\xce\x90\x04\xa5\x02\x37\x48\x88\xff\x06\xba\x8d\x38\x41\x61\x14\xe1\x04\xa2\xcf\xc6\xb3\x0c\x04\x08\x3d\x0a\x1f\xc4\x4c\xa4\x3a\x46\x4a\x86\xec\x81\xb6\x62\x04\xa4\xe3\x1a\x3f\xb9\x46\x68\xa9\xb1\x08\x09\xc4\x92\x56\x32\x2e\xd2\x47\x86\x52\xc1\x50\x2a\x68\x34\xf6\xdb\xc1\x11\xcc\x27\xbd\x06\x9c\xfa\x01\x1a\xc4\x51\x9a\xf9\x91\xde\xbc\x35\x89\x94\x3a\xc7\x6e\xc5\x9a\xc0\xfb\x34\x3c\x43\xbf\x6f\xa0\xda\x75\x7b\x40\xff\x67\x73\x87\x31\x0a\x37\x3b\xf4\x7f\xc5\x9a\xb1\x58\xd3\x89\x85\xda\xb3\x8d\x22\xff\x82\x38\x64\xb0\x03\x3d\x46\x14\x32\xc1\xc4\x1f\x24\x12\x59\x41\xbe\x32\x1b\x33\xb6\x0c\x24\x74\xda\xc6\xc7\x1d\x7a\x52\x55\x5f\x9c\x2f\x98\xbb\x45\x20\x83\x61\xfe\x6e\xe2\x8f\xed\x6f\xf6\x58\xf4\x31\xc0\x2b\x84\x25\x56\x18\x09\x65\xc1\x29\x2f\x13\x88\xcc\x28\xfd\xf0\xc1\xc8\x64\x92\xe0\xad\xcc\x0d\x3e\xf6\x58\xd1\xc3\x60\xa8\xff\xa7\x47\x0f\x9b\x23\xa6\x2e\x22\x22\x12\x1e\x9a\xd3\xd0\xdc\x08\x62\xee\x1a\x73\xa3\x88\xb9\xab\x3e\x52\x24\xb1\xfb\x73\xbb\x1e\x55\x4f\xc3\x78\x5b\xf6\x63\x22\x5d\xec\xda\x83\xa3\x15\x06\x1c\x2b\xe4\x98\xf2\x58\x69\x40\x73\x09\x85\x4b\x1a\xfc\x92\x49\xa0\x52\x75\x86\x1c\x9b\xf8\x03\xfb\x25\x91\x38\xf8\x3b\x8c\xe0\x5e\xfd\xad\x15\xe6\xd7\x9d\xd6\xb2\xe5\xf5\x38\xec\x2f\x13\x54\x02\xb0\x6d\x4d\xb5\xaf\x38\x1a\x2c\x83\x4d\xa3\xe5\x3d\x75\xb3\xd4\x3e\x4c\x82\xf6\x7c\xe3\xbb\x74\xe4\x37\xda\x3a\x48\xf2\xb2\xa1\x83\x4b\x47\x7e\xbb\xde\x30\x5f\x36\xd7\x2d\x25\x9b\xda\xab\x24\x9c\xe2\x49\x50\xef\xd4\xac\xb6\x7f\xca\xab\x69\xff\x73\x30\xd4\xdb\xc1\x97\xd3\xcf\xc1\xb0\xe8\xde\x41\xed\x7a\x1c\xe0\xe5\xc1\xb0\x6f\x7d\x9d\x25\x8e\xd7\xcb\x17\x63\x3f\x98\xf8\x91\xed\x73\x6c\x07\x86\x07\xfa\xeb\xa9\x1f\x2c\xfb\x51\x1a\x5e\xbf\x6a\xe8\x83\x40\x3e\x85\x69\x5c\xaf\xd5\x1b\xfa\x88\xb3\x4f\xaf\xd6\x5e\xad\xe9\x33\x44\x3e\x7d\xc1\x49\xcc\x5c\xaf\x2d\x5f\x23\xc7\x37\xaa\x23\x5b\x1e\xe1\x6b\xed\x83\x8f\x75\xe2\xa2\x71\x37\x02\xe3\x7d\x32\xd0\x27\x37\xf1\xfb\xfd\x30\xb3\xbe\x5c\x1e\xe3\x0b\x7f\x70\xf3\xd8\x77\x40\x62\xf5\xc0\x93\xbe\x68\xe0\x65\xbe\x56\xc4\x23\x5b\x22\xf0\x4c\x56\x86\x66\x16\xca\xd6\x81\xf8\xdd\x68\x89\xdf\x84\xea\xf9\x6f\x42\xec\xe2\x37\xfd\x95\x93\x76\x6e\x5f\x0a\xbf\x18\x21\x53\x0c\x28\xfd\x1a\x77\x58\x14\x1d\x4e\xad\xd2\x53\x96\xa8\x4f\x82\x36\xf3\xb7\xb1\x52\x83\x50\x22\x6d\x56\x26\x40\xf1\x46\xd0\x9d\xfc\x86\x92\x9b\x78\x23\x53\x99\x78\x19\xa9\xaf\x24\x9a\x82\x67\x42\x4a\xf0\x23\xa7\x20\x3a\x2a\x03\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\xb6\xeb\x5e\xb1\x36\xd9\x53\xe9\xaa\xdb\x6e\x79\x0a\xe1\x75\xdb\x6d\x2f\x9f\xf8\x6e\xbb\xe3\xa9\xa3\xd7\x6d\xaf\xe9\x37\xc2\x3a\x29\x77\x3b\x35\x8f\x51\x6b\xb7\x03\xf8\x08\x4a\xe9\x76\x1a\x9e\x4c\x2b\xdd\x4e\xcb\xb3\x51\x4b\xb7\xd3\xf4\x64\x0a\xe9\x76\xda\x9e\x4c\x3f\xdd\x0e\xe0\xa5\xd0\x4c\xb7\xb3\xe6\xe9\x54\xd3\xed\xac\x7b\x3a\xdd\x74\x3b\xaf\x3c\x83\x48\xba\x6b\x35\xcf\x42\x4e\xdd\x35\xc0\x9f\x2d\x89\xee\x1a\x60\xcf\x48\xa3\xbb\xd6\xf2\x0c\xe2\xe8\xae\x01\xe2\x84\x8c\xba\x6b\x80\x73\xbe\xce\xba\x6b\x1d\xf9\x02\xdd\xcb\x97\x6c\x77\x8d\x5f\xad\x93\xc5\xdc\x5d\x7b\xe5\xf1\xa5\xda\x5d\xaf\x79\xf9\x12\xee\xae\xd7\xbd\x7c\x71\x77\xd7\x01\x9d\x9c\x82\xbb\xeb\xd0\xb8\x60\x34\xdd\xf5\xd6\xed\x99\xd7\xa9\x3d\x5d\x1e\xfc\xf5\x97\x07\xbd\x11\x1e\x7c\x26\x9d\x82\x95\x42\xdd\x80\x68\x9a\xb3\x74\x36\x25\x03\x83\x59\x7c\x6a\xa9\xdf\x20\xc7\xd3\x90\xe6\xe8\x87\x0d\xf4\x82\x43\x7e\x61\xb1\x08\x11\x4e\x1a\x0f\x78\x5d\x51\x68\x8e\x2f\xda\x39\xc2\x43\x9c\x60\x38\xe8\x25\xe1\x05\x9c\xc9\xc2\x28\xcc\x72\x30\xe9\x6c\x8a\x13\x50\x5d\x6f\x68\xe9\x39\x24\x28\x9b\xb3\x8b\x09\x8e\x32\xad\x00\xca\x62\x34\xf2\xa3\x60\x8c\x95\x71\x93\x61\xf7\xad\x90\x15\x9b\x1a\xa8\x6a\xba\x03\x4a\xba\x6f\x1a\x4b\x9e\x9a\x40\x85\x51\xb6\x2e\x69\xe8\x87\x72\x7d\xa1\x98\x50\x67\xc7\x3c\xe6\xe7\x35\xa8\x12\xfe\x13\x81\x0a\x2f\x64\x6c\x94\x43\x84\x15\xb1\x98\xa6\xff\x02\x48\x97\x21\xbe\x72\xa1\xe8\x6c\x5e\x42\x78\x8f\xa3\x80\xbe\x7e\x55\xcb\x73\x82\x03\x2c\x41\x67\xcc\xab\xff\x40\xd6\x9c\xb0\x1d\x81\x45\x67\x07\x6e\x54\xad\x1a\xad\x38\xb1\xaa\x77\xec\x68\xb9\x5b\x5a\xac\xc6\x5e\x94\x35\x1b\x8b\x36\xb1\x58\x8d\x9d\x71\xec\xdf\xa5\x4a\xa7\x05\xef\xf3\xf2\x77\x24\xa5\x15\x4a\xc1\x1e\x92\x5f\xdd\x64\xf8\x00\x92\x03\x19\xaf\x6d\x79\x97\x15\xfa\xdb\xa5\x8b\x2e\x6f\xab\xcc\x8a\xc8\x4b\x2f\xa6\x42\xc8\xa1\xbd\x15\xb8\xa1\x0d\x3b\xce\x16\xcd\xc2\xf6\x35\xcb\xbe\x7a\x93\xd9\x8c\x9f\x17\x72\x17\xb4\xa1\xb2\x48\x3e\xed\xbc\xfe\x69\x78\x76\xa7\xe4\xd9\xb9\x39\x77\xf8\x05\x53\x55\x6d\xee\x38\xaa\x16\x15\x8c\x35\x4f\x6d\xe1\x21\xe6\x46\x68\xeb\x88\x32\xdf\xd6\xac\x67\x64\x34\xc9\x6b\x02\x0f\x45\x44\xea\x93\x99\xb9\xd9\xae\x3f\x9d\x8e\x6f\x58\xc3\x7e\x72\x31\x23\x2c\x3c\x2d\xf2\x57\x64\xfc\x7a\x65\x9a\xc4\x59\x4c\x70\x94\x39\x77\x91\xe1\x84\xbe\xfb\xd8\x15\x2c\x9d\xfa\x93\xac\xf3\xd7\xc8\x3a\x10\x30\xfa\x2f\x88\x4b\x64\xcd\xa9\x54\xc2\x44\x02\xb6\x58\x7a\x8f\x87\xb2\x5c\xb7\x4e\xaa\x9c\x30\x66\x21\x95\xa4\xaa\x4b\xed\xe6\xcf\x26\xe9\xb9\xf8\x4a\xa7\x65\xe7\x22\x27\x84\x4d\x6c\xd0\xe1\x5b\xf1\xfb\x29\xfd\x91\x86\x11\x0b\xc6\x4a\x58\x46\xed\xba\x5e\x63\x7f\x55\xf4\x55\x4d\xe3\xcb\x96\x57\xa5\x6a\xb5\x50\xdf\xdf\x6a\x6b\xd6\x14\x36\x03\x10\xdd\x6b\x12\x6d\xb0\x51\xb5\x18\x80\xf0\xb4\x37\x85\xb7\x63\xb9\x26\xd8\x9e\xab\xf8\xd4\xe4\xa4\xb5\xeb\xce\x5a\xab\xdd\x68\xd6\xea\x1e\xaa\x5d\xe3\xe1\x20\xf0\xfb\xeb\xaf\x2c\x79\x15\x6b\xd7\xaf\xd6\xfb\x7e\x30\x18\x62\x0f\x06\xa6\xd9\x68\xb7\xd6\x3a\x6a\xb9\x33\xe7\x8d\x98\x96\x46\x4f\xee\xc5\xbe\xc8\xa4\x67\xdb\xbb\xae\xfc\x29\xc2\xe0\x5e\x3d\x7f\x0f\xa9\x77\xdc\x3b\x86\xfb\xfa\x9a\xcf\x06\x45\xe2\x9c\xc0\xe3\xe9\x05\x51\xe8\x88\xc0\xbb\x7f\x2e\x95\xde\x3f\xe5\x0f\x67\x36\x97\x10\xe9\x33\x21\x38\xb3\x00\xf9\xab\x54\x2a\x12\x4c\xea\x29\x8e\xbe\x22\xf9\x25\xec\x75\xad\xaa\xe6\x23\x8e\xbe\x96\x04\xd8\x68\x55\x2d\x00\x21\x94\xb1\xe2\x92\x6e\x82\xbb\x9f\x71\xc8\xae\x72\x43\x61\xbf\xee\x57\x86\xb4\x86\xa4\x31\x45\x4b\xa8\xa6\x8b\x0f\x4a\xe9\xba\x56\xba\x5e\x58\xba\xa1\x95\x6e\x14\x96\x6e\x6a\xa5\x9b\x85\xa5\x5b\x5a\xe9\x56\x61\xe9\xb6\x56\xba\x5d\x58\xba\xa3\x95\xee\x14\x96\x5e\xd3\x4a\xaf\x15\x96\x5e\xd7\x4a\xaf\x17\x96\x7e\xa5\x95\x7e\x55\x3c\x3b\x35\x6d\x76\xe6\x4c\x66\x5d\x2b\x5e\x3c\x9b\xf5\x86\x56\xbc\x78\x3a\xeb\x4d\xad\x78\xf1\x7c\xd6\x5b\x5a\xf1\xe2\x09\xad\xb7\xb5\xe2\x6d\x83\x1b\xac\xae\x12\x86\xfc\x39\x8c\x2e\x48\xd5\xd0\x1f\xf7\x6d\x62\xb3\x4f\xb6\x81\x53\xeb\x40\xf5\xe1\x93\x75\x50\x06\xf0\xc9\x3a\x00\x01\x7c\x6a\xda\xd0\xe9\xe5\x77\xd0\xea\x37\x82\xc4\xce\x4e\xc5\xf7\x50\xdf\x43\x03\x0f\x05\x9e\xb4\x40\x3d\x84\xd6\x3c\xb2\x85\xd6\xce\x74\xde\x10\xd0\x7a\x81\x87\x44\xd5\x7c\x84\x3c\x84\xea\x0d\x0f\x9d\x9c\xd6\x8d\x7a\x03\x5a\x8f\xb6\x44\xab\xe6\x8b\x96\xd4\x5b\x23\xf5\x1a\x46\xbd\x3e\xad\x27\x90\xf4\xa5\x7a\x4d\x0f\xa1\x06\xb4\xd7\x34\xea\x15\xf5\xaf\x25\xfa\xd7\x5a\xa8\x7f\x6d\xd1\xbf\xf6\x42\xfd\xeb\x88\xfe\x75\x16\xea\xdf\x9a\xe8\xdf\xda\x42\xfd\x5b\x17\xfd\x5b\x5f\xa8\x7f\xaf\x44\xff\x5e\x2d\xd4\xbf\x7a\xcd\x63\xfd\xab\x9b\x04\x53\xd4\xc1\x7a\xdd\x63\x1d\xac\x9b\x14\x53\xd4\x43\x82\x25\xed\x61\xdd\x24\x99\x42\x12\x6d\x7a\x9c\x44\x4d\x9a\x29\xec\x63\x4b\xf4\xd1\x24\x9a\xc2\x3e\xb6\x45\x1f\x81\x6a\xcc\x4e\xbe\x7b\xe7\xe8\xa4\x87\x50\x9b\x76\xd2\xa4\x9b\x80\x56\xb4\x76\x92\xd0\xdb\x2b\x5a\xd1\x24\x9c\x01\xad\x68\xef\x64\xdd\x43\xa4\xa3\x27\xa7\x75\x93\x72\xfa\xb4\xa2\xb5\x93\x84\x63\x34\x6a\x50\xd1\x24\x9d\xa2\x3e\xb6\x45\x1f\x1b\x76\x5e\xe3\xea\x23\xa1\x39\xda\xc7\x86\x9d\xd9\x38\xfb\xd8\xe6\x7d\x6c\xd8\xb9\x8d\xab\x8f\x2d\xd1\xc7\x86\x9d\xdd\xb8\xfa\xf8\x2a\xef\xa3\x9d\xdf\x38\xfb\xd8\x12\x7d\xb4\x33\x1c\x57\x1f\x09\x63\x64\x7d\xb4\x73\x1c\x57\x1f\xd7\xf3\x3e\xda\x59\x8e\x93\x56\x9b\x1e\xef\xa3\x9d\xe7\xb8\xfa\xd8\x10\xb4\xda\xb0\x33\x1d\x57\x1f\xd7\x44\x1f\x9b\x76\xa6\xe3\xea\x23\x59\xfe\xb4\x8f\xcd\xba\x7d\x41\xee\xee\xba\x89\xb5\x05\xb8\x36\xed\x5c\x67\x77\xd7\xde\x49\x32\xac\x64\x6d\x9d\x9c\x36\xed\x5c\x67\x77\xb7\x60\x41\x76\xa0\xa2\x9d\xeb\xec\xee\x3a\x3a\xd9\xf2\x50\xa3\x09\x15\x4d\xd2\x29\xea\x63\x3d\xef\xa3\x9d\xe9\xb8\xfa\xd8\xca\xfb\x68\x67\x3a\xae\x3e\xc2\x44\xd2\x3e\xda\x99\x8e\xb3\x8f\x35\xd1\x47\x3b\xd3\x71\xf6\xb1\xe9\xb1\x3e\xb6\xec\x4c\xc7\xd5\xc7\x9a\xe8\x63\xcb\xce\x74\x5c\x7d\x6c\x8a\x3e\xb6\xec\x4c\xc7\xd5\x47\xc2\xca\x69\x1f\x5b\x76\xa6\xe3\xea\xe3\x2b\x31\x8f\x2d\x3b\xd3\x71\xf5\x91\x2c\x0f\xd6\x47\x3b\xd3\x71\xd2\x6a\x9b\xd3\x6a\xcb\xce\x74\x5c\x7d\x6c\xe4\x7d\x5c\xb3\x2f\xc8\xbd\x3d\xb7\xa0\xda\xa1\x9d\xb4\x73\x9d\xbd\x3d\x7b\x27\x81\xe6\x80\x07\xb4\xec\x5c\x67\x6f\xaf\x40\x0c\x68\x83\x08\x68\xe7\x3a\x7b\x7b\xf6\x4e\x12\xde\xd1\x80\x61\x6d\xdb\x45\x1d\x57\x1f\xc9\x7c\xd0\x3e\xb6\xed\x4c\xc7\xd5\xc7\xa6\xe8\x63\xdb\xce\x74\x9c\x7d\xac\x89\x3e\xda\x99\x8e\xab\x8f\xf5\xbc\x8f\x76\xa6\xe3\xea\xe3\xba\x98\xc7\xb6\x9d\xe9\xb8\xfa\x08\x34\x47\xfb\x68\x67\x3a\xae\x3e\x82\x48\x4e\xfb\x68\x67\x3a\xce\x3e\x36\x3d\xde\x47\x3b\xd3\x71\xf5\xb1\x25\xfa\xd8\xb1\x33\x1d\x67\x1f\xeb\xbc\x8f\x1d\x3b\xd3\x71\xf5\xb1\x21\xfa\xd8\xb1\x33\x1d\x57\x1f\x5f\x89\x79\xec\x34\xcd\x05\x09\xd7\x28\x19\x4e\x26\x38\x08\xfd\x8c\x39\x95\x81\xbb\x82\x5a\x8e\x1c\x71\xd1\x06\xaa\xc0\x7f\x97\x90\xaf\x6b\x58\x69\x99\x3a\x2b\x53\x27\x65\xfa\xf6\x32\x0d\x56\xa6\x41\xca\x0c\xec\x65\x9a\xac\x4c\x93\x94\x09\x0c\x6d\xae\xa6\xaa\xdc\xb1\x58\xea\x2e\x18\xd0\x16\x32\xa5\x8b\x6c\xba\x7e\xe6\xdb\x0e\xe6\x7e\xe6\x8b\x50\x3e\x7e\xe6\xbb\x95\x63\xd1\xdb\x30\x4b\x4f\xe2\xcc\x1f\x0b\x98\xd1\x96\x9f\xf9\xd4\x83\xe4\x25\x5a\xb7\x40\x87\x3a\x1f\xf0\x30\xe3\xd0\x85\xc7\x09\x94\x37\x3a\xe3\x4c\x79\x25\xd0\x3c\xcd\x41\xfe\xfc\xf3\xcf\xa8\x0d\x17\x6f\xb5\xeb\xf5\x5a\x7e\xdf\x96\x97\xf8\x17\x6a\x36\x0c\xe2\x50\xfb\xb2\x8b\x36\x10\xa8\xdd\x87\xe3\x38\x4e\x2a\x52\x27\x57\x15\xdd\xbb\xab\x73\x50\xf6\x03\xda\x90\x9e\xf4\x85\x23\x50\xaf\x54\x2a\x39\x6e\x4b\xa8\xd3\xa2\xf9\xd2\x5e\x41\x30\xd1\x56\x95\x2a\x6c\xec\xfa\x59\x5e\x95\xe1\x9c\x2b\x67\xe5\xb7\xe5\xb5\xb3\x26\x38\xa6\x9a\xd5\xc1\xcd\xd3\xcd\x1a\x5c\x62\x91\xce\xb6\xca\x74\xf6\x83\xb5\xb3\x1f\xee\xda\xd9\x0f\xd6\xce\x7e\x28\xdb\x59\xb3\xb7\xb2\x13\x55\x45\x74\x9f\x07\x9b\x82\x9c\x7a\x76\xff\x41\x30\x78\xa7\x6e\x0c\xe0\xa3\x68\xf3\xa4\x2a\xcc\x2b\x3f\xc7\x1b\x52\xd1\x79\x5b\xc8\x77\x97\x19\xc6\x3b\xbd\xdf\x16\xba\xf7\x70\x5c\x71\xa1\xa2\xeb\x7f\x81\x09\x5c\x61\xec\x9e\xda\xef\x2e\x76\xd9\x2d\x59\xa5\xb2\xab\x5c\x4b\xec\x2e\x7c\x1f\x41\x69\x61\x57\xb9\x8b\xd8\x75\x5e\x42\xcc\xbf\x71\x38\x62\xb9\x81\x61\x0e\x59\x04\x9e\x00\xc6\x54\x2d\x5a\x22\x59\x39\xb8\x21\x14\xb2\x7a\x50\xb0\x82\x53\xa6\xb8\xa1\x83\xc7\xfc\xfa\xdf\xd8\x78\xe1\xf3\xb9\x41\x0b\x2e\xef\x4a\x1e\x41\x83\x7c\xb5\x7b\x38\xd0\x5f\x02\x49\x4d\xf5\x75\xed\xa1\xd4\x43\xea\x15\x1a\xf0\x49\xb4\x81\x7c\xb4\x84\x2a\x95\x3e\xfa\x89\x6e\x8e\x95\xff\x4b\x7e\x06\x55\xc2\x06\xae\xd1\x12\xca\xa4\xf6\x44\xc0\xe2\x88\x4c\x53\x4a\x57\x2a\x8d\x53\xde\x6c\xa0\x65\x94\x56\xa1\x5a\x5f\x33\x7a\x13\x58\x69\xe7\xff\x72\x58\xc1\x76\x5c\x19\xa0\x9f\xd0\xff\x7d\x1c\xac\xb4\x43\xd0\x5c\xac\xfa\xe8\x77\x34\x40\xbf\x13\xc4\x1e\x1e\x19\x4d\x00\x9c\x8b\x0c\x41\xa4\xd2\x47\x5f\x1f\x78\x70\xe4\xdb\xea\x63\x57\x9a\xf4\xb9\x89\xf7\xcb\x04\x59\xe3\x7e\x62\x9a\x8b\x22\xac\x06\x13\x8c\xc3\x59\xcc\x51\xfa\xae\x61\xcd\xd8\xba\x14\x46\x2e\xfb\x5b\x6d\x8b\xef\x57\x71\x79\xd3\xe1\x2b\x8f\x2f\xa6\x5c\xe6\xab\x19\xf9\xf7\xb7\xda\x56\x93\x01\xe7\x24\xcc\xc9\x55\xff\x50\x53\x70\xa7\xd0\x0e\xf3\x27\x4e\xf6\xf2\x7b\x88\x89\xa3\x4e\x65\x62\x22\x76\x27\xfe\x80\x4c\x86\x92\x19\xde\x9c\x0f\x56\xcc\x9c\x93\x3c\x9b\x3d\x9d\x97\xc2\x0c\xec\x2c\xb2\xb5\xc3\x02\xaa\xf1\xb7\x76\x31\xfb\xe7\xc7\x64\xa3\x8b\xed\x25\x8b\x33\x84\x76\x30\x0e\xfa\xfe\xe0\x33\x8b\xab\x39\x89\x03\x58\x52\x84\x66\xc4\x7c\xc3\xcb\xde\xce\x5b\x22\x02\x59\xc4\x03\x30\x73\x82\xaf\x8a\xb5\x1c\x58\xb8\xd0\x56\xf6\x09\x00\x66\xcc\x23\x56\x7d\x6f\xe7\xed\xca\x76\x44\x63\x95\x83\x01\xd5\xce\x5b\x8b\xc1\xcf\xd4\x61\x2e\xc3\xcc\x0c\x0b\x4c\x66\xdc\xa2\x29\x0b\x41\xc5\x05\x12\xfa\x68\xbb\x67\x96\x42\x79\xd0\x42\x72\x28\x0f\xb5\x3c\x8f\x51\xfe\x1e\xdf\xa4\x59\x82\xfd\xc9\x66\x14\xb0\xde\x59\xac\x23\x63\x66\x16\x2b\xc0\x79\xac\x01\x9b\x90\x7d\x84\x27\x18\x82\x8c\x83\x31\x26\x9d\x27\x16\x2b\x13\xfc\xe7\x23\x7c\x9d\xd1\xd7\x76\xf1\x1d\x5f\xbe\x65\x31\x53\xa1\xf5\x95\x74\x1c\x0e\x70\x85\xa3\x20\x6e\xea\x05\x2e\x36\xfb\x49\x65\xd6\xb6\xf0\x3f\x65\xd6\xee\x31\xba\x60\x38\x3c\x0a\xd3\x85\xc7\xf6\x9b\xd1\xcd\x49\xde\xa1\x3e\x1e\xc4\x13\xe6\x75\x4f\x08\x22\x8c\x67\x69\x39\x92\x11\x5d\x2c\x25\x8e\x17\xf4\xa6\x32\xb7\x0b\x9a\x6f\x84\x79\x60\x83\xf3\xde\x65\x1e\xac\xe5\xf2\xb5\x6a\x34\x2e\x87\x63\xa6\xcd\xe7\x9f\x21\xb3\xeb\xa5\xf5\x48\x23\x4a\xa3\x0d\x14\x5e\xb2\x29\xac\x39\x56\x62\x7c\x89\xd1\xde\x2f\x70\xfe\x4c\x67\xfd\x14\xff\xf7\x0c\x47\x59\xc1\xe9\x19\xf0\x15\x0e\x0c\x73\x0d\xa0\x75\x7c\xb4\x09\x31\x27\x81\xfc\x31\x2a\xc7\x74\xa0\xa1\x60\x45\x00\xf1\x90\xda\x95\xd5\x55\xc4\x66\x24\x7f\x67\xcd\x96\x5b\x1c\x35\x86\x9a\x9e\xe7\x16\x82\x10\x09\x46\x34\x0a\xe7\x68\x83\x5e\x18\x16\x5c\x9c\xd8\x79\x5b\x64\x70\xcd\x37\x9d\x45\xe2\xd4\x75\x9a\x4f\xc2\xc7\xf7\x2e\x7c\xa0\xff\x9c\x26\x38\xc5\xc9\x25\xa6\x62\x48\x3c\x23\xa2\xbc\x24\x7e\x80\x1a\xc3\xcf\xc2\xfe\x98\x71\x60\xb4\x95\xa0\xb7\x49\xe8\x47\xe8\x1d\x75\xcf\x44\xc3\x70\x8c\x71\x34\x58\x19\x00\x08\x1e\xf2\x19\x22\x60\x6b\xf4\x73\x72\x04\x45\xfe\xcb\x8f\xd0\x6e\x32\xeb\xdf\xa0\x3f\x46\xe4\x3f\x2b\x57\xb8\xff\x9f\x17\x13\x3f\x1c\xaf\x0c\xe2\x89\x5d\xde\x39\x39\xe2\xcd\x15\x88\x3d\x72\xa1\xd2\xd2\xcf\xb3\x3c\xdf\x4b\x34\x20\x07\x05\x9a\x32\xe9\xf9\xb3\x67\x64\xd0\x81\xf4\x44\x3a\x24\x50\x12\x51\xa5\x50\x15\x66\x9d\xfe\xfa\x13\xad\xae\xc6\x97\x38\x19\x8e\xe3\x2b\x52\x07\x36\xbe\x3a\x4f\x07\x4a\xea\xd5\x3b\xd5\x9f\x48\xd9\xd7\xe2\x73\x43\xfe\xbc\xae\x7f\x6d\xb2\x3d\x8c\x35\x06\x78\x02\x2a\x04\xac\x68\x77\x75\x15\xf1\x66\x51\xbf\x4e\x8a\x00\xca\xd0\x74\xed\xb5\xa8\xd2\xc8\xab\x88\x32\xcf\x00\x01\x5a\x88\x96\x6a\xaa\xa5\x58\xb1\x67\x80\x0a\x2b\x77\x0b\xff\x12\x82\x94\x4b\x2c\x2d\xf5\x9b\xd2\x77\xf8\x87\x97\xa1\x45\x96\x96\xfa\x8d\xd7\xcf\xdd\x05\x96\x96\xfa\x75\xf6\x9d\xfc\x0b\x1d\xe7\x8d\xc2\xc3\xd2\x06\xf4\xfc\xcd\x1b\x96\x0f\x52\x7e\xdd\xa0\x2a\x40\xe5\x2d\x43\xc8\x6c\x49\x54\xab\x5d\xd7\xea\x4c\xeb\x97\x17\x65\x5c\x8f\x14\x22\x2f\x6f\x75\xea\x60\xcb\xa3\x32\xa0\xff\x55\x69\x84\xbd\xa4\x37\x48\x9c\x94\xf2\x97\x55\x46\x30\xd2\x14\xac\xae\x22\xb2\x4b\xc0\x4d\x0c\x0a\xa5\x85\x44\x17\x8f\xb1\xd2\x5e\xa4\x08\xe0\xa5\x28\x8e\xc6\x37\x74\x39\x6e\xfd\x7a\x70\xb4\x85\xfe\x40\x6f\xd0\x3a\xc0\xe4\x0d\xd6\x6d\x58\xd0\xbb\x38\xb5\xb3\xec\x1b\xef\x2f\x5f\x4b\xca\x59\x40\xac\xab\x15\xc7\xeb\xbf\x50\xe6\x5c\x54\xe4\x34\x8a\x6b\x32\x8c\xd9\x2a\xe3\x89\xa2\x59\x3e\x60\x06\xea\x45\x12\x0f\x72\x4b\x3d\x20\x34\xd8\x1b\x29\x96\x81\xd0\x1d\xe4\x20\x34\x5f\x16\xe2\xd2\x01\x21\x6c\x93\xe6\x29\x2b\x7a\xa6\x8b\x46\xec\xb3\x84\xab\xaa\x7a\x5e\x44\x28\x42\x0e\xc1\x08\xdd\x4d\x38\x42\x0b\x0a\x48\x48\x95\xe7\xcc\x43\x57\x4e\xf7\xf2\xd9\x4b\x2c\x8d\xd7\x9a\x64\x25\x8a\x4b\x02\x96\x53\xc4\x92\x0a\x2f\x20\x69\xb5\x9e\x24\xad\xef\x5d\xd2\x72\xc8\x57\x0e\xf5\xce\xc9\x51\xb1\x9c\xb3\xa8\x7a\xc7\xc2\xd2\x75\x5e\xfe\xc4\xc4\xff\x79\x4c\xbc\xf0\x34\xfb\x08\x2c\x7b\x2f\x1a\x24\x18\x22\x37\x30\xe0\x1a\x48\x26\x87\xe4\x93\xbb\x8c\xa8\x31\x8d\xe3\x0b\xdc\x96\x7f\x45\xb5\xbf\xd5\xe6\x50\x76\x57\x98\x7f\xde\x26\x65\x16\xd8\x05\xda\x4f\xbb\xc0\xdf\x62\x17\xd8\x1e\xe3\x41\x96\xc4\x51\x38\x40\xbd\x38\xc0\xfd\x38\x9e\xaf\xf0\xdf\xee\x15\x29\xfc\xe9\xd7\x85\x76\x84\xed\x9e\xaa\xf0\x27\xcf\x0f\xb5\x03\xc8\xac\x5d\x65\x20\x6a\xbd\x22\x2d\x26\xc1\x47\x59\x48\x8f\x85\x5f\x80\xef\x84\x1f\x4f\xbd\xd4\x9b\xaf\x37\x83\x32\x0b\xac\xe3\xbf\x77\x72\xe4\xff\x39\xeb\xf8\x60\x96\x4d\x67\x59\xf9\x4b\xbb\x83\xc2\x4b\xbb\x83\xc5\x2f\xed\x74\xa9\xee\x40\xbb\xc4\x3b\xf8\x6b\xaf\x83\x1e\x5d\xaa\x33\x75\xf3\xe2\xcd\xc3\x4a\x76\x05\x0d\x7d\x2f\xd2\xdd\x3f\xe9\x84\x7d\xa0\x5d\x6b\xba\x84\xa8\x83\x12\x97\x16\x07\x0b\x5e\x5a\x3c\x65\xb1\xfb\x7b\x30\xdf\xcd\x8f\xc7\x7b\xe8\xb7\x95\x57\x8d\x26\x37\x10\x47\x69\x46\x96\xf7\xc5\x8d\xc1\x7d\xa7\x7e\xb0\xb2\x19\xa5\xe1\x6f\xa4\xb4\xc8\x05\x37\xf5\x03\x99\xfd\x05\x7e\xe6\x4b\x17\xa1\xae\x0b\xd0\x54\xbd\x01\x25\xb5\x8e\x73\x83\x5f\xc5\x00\xf8\xb5\x5a\xb4\xaf\xa7\x15\xe9\xbb\x12\x8a\x00\x51\xcc\xa2\x4c\xf4\x4c\x0b\x66\x05\xb6\x78\x87\xf4\x9b\x01\x8c\xbe\x58\x56\x31\xfb\x97\xf6\xdd\x68\x8d\xc6\xb4\x19\xfb\x29\x8d\x9c\x85\xa6\x71\x1a\xaa\x1e\xf8\xa4\x51\xf2\x9d\xd4\x3f\x8c\x79\x67\x45\x0b\x4b\x1a\x46\xcb\xa8\xae\x35\x72\xe8\x07\xf9\x33\x0c\x94\xc8\x36\xa2\xbe\xa6\xac\x44\x6e\x2b\x0f\xa9\xa5\x36\x92\x87\xd4\x92\x4b\xdb\x82\x6b\xa9\x96\xd9\x4b\x1a\x20\x6e\x87\xc8\x2d\x70\x67\x91\x85\x38\x74\x8a\x78\x87\x33\x29\xe1\xbc\x32\x55\x54\x81\x2f\x46\xb3\x78\xe6\xa4\x3e\x57\x54\x34\x97\xc9\xf1\x97\xf5\x3d\xbf\x08\x92\x50\x60\xfb\x8a\xe1\x21\xa1\x81\x71\xf4\xf6\xf9\xb3\x5b\x2b\xdf\xe4\xcb\xe5\xfa\x55\xa3\xb9\x10\xef\xbc\x5f\x62\xb2\x27\xde\xf9\xad\x78\xe7\xde\xf1\x01\x82\x90\xb8\xe5\x58\xe7\x1e\x0b\xa0\x7b\x5f\xd6\xf9\x97\xb3\xc3\x7c\x49\xcc\xe1\x87\x16\x56\x45\xd3\x01\xd8\x23\xd0\xad\x24\x7e\x14\xc4\x93\x8a\xc1\x01\xab\xd5\x15\x4d\x52\x2a\x86\xc3\x52\x87\x9d\x1a\x5c\xae\xd1\x3a\xf3\x08\xb8\x27\x46\xa5\x33\x2a\x4e\x9c\x0b\x31\xaa\xbf\x77\xe6\x85\xff\x51\x8c\x6a\x75\x6f\xbb\x87\x5e\xad\xbd\x5a\x5b\xae\x23\x46\x1b\x68\x1f\x67\xa3\x38\x40\x0d\x17\xb7\x82\xd0\xde\x77\xe5\x56\x9b\x41\x40\xfd\x07\xd5\x05\x51\x82\x0b\xf0\xd5\x4b\x6a\xd3\x3f\xbe\x68\x95\x06\xfe\x0f\x4e\x62\xc8\x1d\x96\x8d\x30\x4a\x70\x2a\xf1\x45\xa5\x23\xa4\x1c\xeb\x31\x79\x36\xf0\xbe\x13\x2f\x60\x0b\xf1\x0b\xc3\x41\x5d\x8d\xce\xe6\x01\x34\x85\x67\x5f\xd8\x71\x84\xd1\x24\x4e\x30\x15\x1e\x97\x97\xa1\x6f\xae\x51\xe4\xeb\x7d\x79\xb9\xe4\x02\x87\xf9\x5c\x64\x81\xaf\xdd\x2f\xca\xf9\xd3\x02\xff\x66\xa7\x38\x14\xc5\xf1\xb4\x9c\x18\xf2\x91\x93\xa3\x73\x65\x0b\x62\x77\xaf\x89\xbc\x48\x11\xcd\x89\xa6\x16\x22\xba\xfb\x85\x9b\x7d\x22\xba\x6f\x45\x74\xff\x47\x62\x7e\xc5\x24\x27\xf1\xc0\xbf\x50\xf8\x2d\x7d\x70\x96\xcf\xb7\x86\x00\x5c\xa9\x14\x8b\xc0\x55\xf4\xf5\xab\xfe\xea\x4e\x5b\x8c\xbd\xc7\xf3\xe3\x0a\xac\xae\xa2\x4f\x04\xbe\x5a\x2f\x34\x22\x05\x80\x66\x41\x94\xb9\x1a\x85\x63\x8c\x2a\x3f\x54\x72\x5f\xeb\x3c\x06\x37\x78\x1c\x1a\x31\xb7\x85\x09\xa7\xa1\xc8\x0c\xc5\x96\x84\x54\x15\xa5\xee\xd8\x0d\xf1\x78\xcb\xec\x5e\x12\x05\x2d\xc4\x4b\xfe\xde\x8e\x5b\x96\x1c\x5d\x34\x49\xd6\xe3\xf2\x95\x3c\x13\x12\xb4\xf6\xd7\xe7\xf9\x78\xdc\x24\xe1\xe5\x62\x62\x1b\x31\xaf\xc5\x97\xe3\xdd\xcd\x7a\x1e\xeb\x99\x3c\x49\x1f\xcd\x44\xe0\x36\x07\xd1\x43\x3f\x4d\xc9\x42\x5e\x26\xa8\x05\xe8\x3d\xbe\x41\x5b\x38\x09\x2f\x69\x4e\xc8\x1d\x3e\x28\x8d\xe2\x98\xd3\x87\x6f\xdf\x6f\xed\x34\xf2\xd6\xc4\x73\xc9\xc4\xe3\xbd\x38\x1a\x86\x17\x33\x96\x89\x32\x86\xac\x90\x69\x51\x7e\xc9\x24\x9e\xe2\x24\xbb\x41\x7f\xd2\x63\x31\x78\x93\x02\xf3\x3d\x19\xd1\x1c\xc7\x29\x79\x08\x23\x96\x2e\x20\x8b\x85\x2f\xcd\x0a\xda\xc2\x43\x7f\x36\xce\xba\xa8\x85\x2a\xf5\xc6\x3a\x24\x52\xae\xba\xe0\x3b\x12\x9a\xe3\x84\x27\x32\xcf\xc1\x91\xf1\x9f\x87\x66\x98\xb1\xe4\x99\x29\x80\xca\x0f\xf5\xd2\x87\x2c\x46\x53\x9c\x0c\xe3\x64\x22\x01\x57\x20\x4b\xe9\x1f\x07\xc3\x8b\xae\x6b\x94\x11\xbd\xf8\x3a\x86\x98\x33\xf5\xc6\xfa\x6a\xb3\xa1\x85\xe0\xa6\x5d\xa1\xa8\x6b\x9f\x72\x84\x94\xc6\x6f\xab\x45\x09\x49\x8b\x12\xc8\x93\x59\x09\x72\xd2\xe2\xeb\x6d\x7e\x16\xd1\x03\xe0\x73\xb7\xa4\xab\x72\xc6\x50\x32\x7e\x03\x1b\xdd\x70\x7f\xb3\x61\x9c\xc0\x29\x26\x6f\xf4\x01\x12\x83\x7e\x0e\x86\x46\xd2\x78\x4a\xed\xfc\xf4\xa8\x98\x61\x2d\x52\xf1\xcf\x7c\xb2\xd6\x69\xfa\xc9\x7b\x83\xf1\xd4\x69\xac\xd5\x6a\x3a\xe0\x82\xec\xf5\x83\xe1\x85\xdd\xf0\x82\x4c\xc4\x86\xf8\xc9\x09\x8f\x14\x77\x05\xc3\x30\xd7\x3b\x5c\x57\x50\x0f\xba\xb2\x2c\xe8\x2e\xf9\x66\xa7\x0c\x36\x50\x0b\x7f\x58\x29\x59\x39\xf5\xc7\x19\xda\x84\xff\x2c\x9e\x88\x96\xbb\xd1\x48\x7e\xed\xf7\x21\x3b\x9a\x48\x3d\x18\xae\xb0\xa8\x24\x15\xde\x19\x0f\xf0\x73\x4e\x2a\x2b\x2e\xcf\xab\x56\x73\xa1\xdc\x2e\xea\xd4\x5b\x0d\x08\xc3\xcc\x91\x14\x96\x79\xd9\x83\xef\x3e\xa3\x55\x42\x3e\x94\x07\x79\x62\x76\xec\x66\x89\xee\x04\xe5\x20\x9b\xd2\xc1\xa6\xe9\xe6\x0d\x7d\x8e\x2d\xd4\x13\xc8\xc9\x7b\x51\x80\xaf\x6d\x35\x4e\x6b\xd7\x4c\x01\x64\x89\xd6\x39\x27\x44\x97\x40\x45\x08\xcb\xe2\x8d\x33\x7f\x7d\x8e\x0d\xaf\x94\xbf\x71\x56\xe2\x5b\xde\x06\x99\x95\x15\xf6\x64\x33\xc2\xc8\xb7\x16\x5a\x34\x7f\x31\xc7\xc8\x42\xfd\xc8\x04\x75\xad\x83\x3c\x2e\xd2\x1b\x8e\x8f\xd5\xb8\x40\x74\x92\xe5\x39\xe6\xc9\xb2\x81\x02\xf3\x34\xbe\x79\xaf\xf5\x39\x43\x2c\xa3\x77\x9e\x1a\xd8\xfc\x3e\x3f\x1b\x03\xc0\x57\x86\xd8\x3a\xba\x66\x71\x91\xc5\x28\x7f\xc5\x3a\xee\x40\x64\x4f\x8c\xb1\x1d\x74\x28\x47\xb3\x63\x60\x2d\x58\x28\xb6\x1c\x75\x6a\xcb\x21\x4d\x9f\xd3\x98\x03\x01\x3f\x57\x9a\x80\xd1\x13\x23\x2d\x7f\xb4\x8d\x75\x99\xf1\x46\xf3\x42\x41\xd9\x3a\xcb\x47\x5f\x7e\x67\x0f\x58\x25\x35\xf1\xdb\xc1\x91\xda\x1d\x70\x9d\xb2\x78\x5c\x1b\xe3\xf6\x07\xb5\x81\xf9\x83\xdb\xc0\x48\xb3\xf9\x1a\xfd\x51\x30\x7a\xe4\x2f\xaf\x71\xfa\x07\x98\xc3\x18\x1d\x39\xfd\x43\x37\x8b\xe1\x7f\xb7\xe6\x6b\x3d\xe0\x14\xf9\x93\x98\x03\xd3\x4d\x43\xa3\xb6\x29\xd1\x98\xc4\x69\xed\x6c\x69\xa9\xd8\xa4\x48\x02\x2e\x1d\x7d\x39\xdf\xb0\x04\x31\x63\x7b\x59\x5e\xaf\xc8\x80\x52\x3e\x46\xdc\x6b\x43\x2f\x13\x6c\xa6\x70\x23\x5f\x70\x13\x7f\x28\xd1\x32\x4c\x6d\xe9\xf6\xe7\x47\xaf\xb1\x88\x06\x0f\x10\xc4\x86\x8a\x08\x42\x32\xa4\x42\xa1\x4b\x4c\x58\xac\x9a\x87\x1c\xb2\xe9\x7d\xc0\x14\xca\xa6\x79\x90\x1d\x71\x94\x74\x09\x30\x1e\xd2\x05\x55\x36\xec\xaa\x58\x4c\x0a\xcd\x11\x9e\x6e\x8b\x6c\xd1\x28\x34\x7b\xa0\x1e\x3d\x85\x2e\xcf\x09\x7b\x7b\xe6\xad\xfd\xbd\x7d\xe8\x17\x48\xeb\x3e\x3f\x39\xfa\xe3\xea\x8e\x9c\xe9\xb5\x5d\x59\xaf\xff\x09\xda\xa5\x63\x30\xce\xec\x71\xe3\x5d\xaa\x44\x92\x5f\x16\xe9\x91\x04\x1e\x47\x78\x96\xfa\xfd\x31\x66\xe1\xc0\x24\x74\x8e\x91\x9c\x6a\x91\x42\xd1\xdf\xbc\x43\x6a\x86\x35\x69\x5b\x38\x82\x6c\xca\x88\x19\xda\x32\x1b\x63\x53\x93\x24\xca\x43\x8c\x95\x30\x45\x3e\xa2\x09\x98\xd1\x25\x4e\x52\x88\x5a\x36\xf2\x33\x14\xe1\x8b\x31\x1e\x64\x38\x20\x6c\x78\xc0\x52\xaa\x66\x4c\xe1\x93\xc5\x68\x1c\x66\xd9\x18\x2f\xd3\x00\x97\x2b\x2a\x50\x9c\x24\x71\x82\x82\x18\xa7\xd1\x8b\x0c\xf9\xc3\x21\x1e\xd0\xba\x14\xa9\x17\x29\x4a\xf1\x60\x96\x84\xd9\x8d\x27\x2a\xf6\x67\x19\x0a\x33\xa8\xc4\x6b\x84\x59\x2a\x02\x2a\x84\xe3\x30\x63\x4e\xdc\x34\xaf\x6b\x48\xf8\xf3\x04\x47\x74\x3f\x48\x6d\x8a\x32\x3a\x20\x1f\x68\xe7\x84\xba\x4c\x7b\x2b\xcf\xdf\x5d\x93\xb6\x15\x1f\x52\xde\xcb\x66\xd0\xce\x03\x46\x6e\xbd\x0d\xa7\x86\xcb\xa2\xd3\x42\xc8\x4e\x68\x64\xf7\xc2\xce\x73\xda\x6f\xa2\x5d\xf2\xcb\x92\x38\xee\xfd\x69\xed\xcc\x43\x95\xf7\xa7\xcd\x33\x16\x2c\x00\x7d\x25\x8f\xec\x2a\xa0\xde\xa9\x5a\x92\xc8\xbd\x3f\xad\xd3\x4a\x35\xb5\x52\xb3\xb8\x52\x83\x56\xaa\xab\x95\x6a\xc5\x95\x9a\xb4\x52\x43\xad\x54\x17\x95\xd4\x3a\xb6\xec\x48\xc6\x90\x71\x2f\x43\xd7\xa0\xf5\xc4\xa0\xf5\xec\x83\x66\xe2\x23\x0d\x17\xeb\x13\xbd\x30\x19\x0e\x79\xda\x41\x8a\x34\x0d\xb2\x5a\xab\x91\x2f\xb6\xfe\x9a\x13\xd1\x54\x21\xd7\xad\x90\x1b\xa5\x20\xd7\x9c\x03\x2f\xc1\xd0\x20\x37\x4b\x41\xae\xbb\x66\xc7\x93\x60\x68\x90\x6b\x1a\xe4\xf9\x13\xd9\xf3\x93\xe4\x06\xf5\xf5\x74\xaa\x74\xaa\xfa\x34\xfe\x85\xa9\xc9\xc8\xe8\xe4\x13\xd6\x93\xde\xa4\x19\x9e\xa0\x61\x3c\x4b\x50\x16\x4e\xf4\xb9\x5f\x30\x28\x6f\x84\xaf\xb3\x63\xb2\xfa\xdc\xf1\x63\x2d\x11\x6f\xf7\xe3\x20\x1c\xde\x50\x4e\x48\xe9\xb0\x04\x16\xeb\x6e\x2c\x7a\xa7\xd4\x71\xe0\xb7\x53\x48\x79\x09\xd1\x56\x8c\x4c\x71\xb6\x24\xb9\xbf\xa0\x14\x67\xb3\xa9\xfa\xa1\xc0\xa3\x63\xfe\x61\x7f\xef\x17\xea\xda\x51\x74\xc2\xdf\xfb\xe5\xbc\x86\x36\xd0\xde\x2f\x66\x6a\x34\xa9\x48\x9d\x16\xa9\x5b\xa3\x19\xcb\x4b\x1a\xa6\x32\x9d\xf5\x2f\x31\x11\x15\x5c\x47\xff\x1a\x0d\x7e\x0c\x6d\xd3\xe8\xc7\x5f\x11\x7d\x72\x45\x3f\x96\x8b\xb3\x30\xc7\xa2\x7c\x7e\x1d\x6a\x0f\x73\x2c\x9a\x6d\x88\x66\xeb\x4a\xb3\xf5\x79\xcd\xd6\xd5\x66\xeb\x8b\x35\x0b\x61\x74\xc2\x1a\x5f\x82\x04\x48\xd8\x50\x57\xa0\xab\x6a\x13\xaa\x36\xf8\x62\x86\xaa\x35\x75\x99\x3a\x66\x84\x91\x75\x11\x6b\x45\x40\xad\x35\x7a\xae\xd7\x63\xfb\xd3\x8f\x75\xfa\xb1\x6e\xfd\xd8\xa0\x1f\x1b\xd6\x8f\x4d\xfa\xb1\x69\xfd\xd8\x2a\x6a\xb3\x5d\xd4\x66\xa7\xa8\xcd\x35\xd1\x66\x81\x46\xaa\x14\xe7\x41\x8b\x73\x1f\x54\x8e\x03\x21\x53\x49\x21\xfb\x11\x3d\x48\x72\x57\xa7\xf2\x5a\x92\x3e\x4a\x71\x66\xb5\x88\xbd\x77\xee\xed\x1d\x06\x37\xf7\x32\x03\x2e\xa4\x96\x3e\xa6\xa1\x86\x7e\x03\x22\x44\x95\xdf\xc8\xdc\xf3\x55\x02\xcf\x62\xef\x7d\xad\x57\xac\xd3\x8a\x0d\x56\x71\x4d\xab\xd8\x76\x56\x6c\xd0\x8a\x2d\x56\xb1\xae\x55\x5c\x73\x56\x6c\xd2\x8a\x9d\x33\x81\x9a\x52\xb1\x9e\x57\xbc\xd7\x2e\x56\x14\xa5\x9e\x22\xc2\x63\xc7\x1f\xb3\x94\xec\x2c\x78\x3c\x3c\xde\x25\x7a\x3c\x87\xc3\x18\x9c\x80\x63\x8b\x1f\x6f\xc5\xd7\xea\x84\x87\xa4\x1c\xbd\xc2\x9b\xee\xb8\xd8\x8b\x4e\xa6\x7e\x61\xc7\x93\xdf\xdc\xe6\x1f\xc3\x4b\xfa\xa5\xd3\x5a\x6d\x36\x74\xb5\x9c\x58\x26\x82\x60\x2b\x25\x5d\xa1\x94\xf5\xa1\x7c\x91\x44\x50\xcd\xe0\xe7\xd8\xbf\xc4\x28\x1e\x07\x4e\x56\xbb\x80\xfc\xd0\x3b\xa7\x93\xdb\xd3\xe3\x1d\x2a\x2d\xf6\xfc\xf1\x60\x36\x26\x2b\x2c\xc2\x57\xce\x66\x7b\x2c\x11\x4c\x8f\x26\x82\xa9\x5d\xb7\x82\x26\xfc\x1f\x5a\xe2\x12\x9a\x9e\xaf\xa5\xc7\xf2\xc2\xf4\x68\x5e\x98\xda\x35\xab\xd1\x84\x98\xf2\x3d\x2e\xa0\xd6\xaa\xe8\x0d\xaa\xf4\xce\xa5\xe7\xff\x40\x75\xd4\x45\xb5\xaa\x09\xb1\xc1\x20\x36\x28\x44\x06\xb0\xc5\x20\xd6\x35\x88\xf5\x12\x10\x9b\x0c\x62\xd3\xe8\x56\x85\xb6\xa3\x40\x6c\x94\x80\xd8\x62\x10\x5b\xd6\x5e\x37\x35\x88\xcd\x12\x10\xdb\x0c\x62\xdb\xda\xeb\x96\x06\xb1\x55\x02\x62\x87\x41\xec\x58\x7b\xdd\xd6\x20\xb6\x4b\x40\x5c\x63\x10\xd7\xac\xbd\xee\x68\x10\x3b\x73\x21\xe6\x62\x3f\x05\xaa\x54\x5f\xd3\xab\xeb\xde\x31\x82\xa6\xc9\xee\x73\xb1\x7c\x8f\x45\x44\x4a\x5d\x5c\x03\xaf\x0e\x49\xd7\x7a\x96\x24\x1c\x3c\x5d\x7e\x32\x1b\x64\x68\x14\x5e\x8c\x90\x1f\x05\x68\x1c\x5f\x21\x3f\xb9\x98\x41\xf8\x17\x70\x73\xfe\xef\x99\x9f\x18\x89\x7b\xa0\x01\x1f\x6d\x90\x56\xb8\x14\x67\x51\x1e\x5c\xf4\x69\x11\xba\x4b\x58\x8f\x4f\xbc\xcf\x0a\x06\x09\x4e\x67\xe3\x0c\xc5\xc3\xa2\xe6\x47\x74\x0b\xa8\x5c\xf8\xe8\x25\xba\xf0\xa9\xeb\x4a\x7d\xad\x8a\x96\x10\x7d\xd5\x67\xaf\xda\xf0\xaa\x0f\xaf\x6c\x48\x8e\x29\x20\xa9\x2b\xf4\x48\xf8\x12\x5d\x5c\xc3\x0c\x57\x81\x20\x78\x01\x21\x76\x4a\x05\x6c\x89\x60\x48\x87\x7e\x3b\x38\x42\x10\x4e\x52\xfe\xf8\x8e\x72\xb8\x8b\x11\xfa\x1d\x5d\x8c\xcb\x32\x39\xbb\x52\xe5\x37\xc6\xe2\xde\x51\x16\x57\xa9\xbc\xcb\xb7\x6f\xb2\x93\xbd\x93\xc4\x82\x2a\x2b\xd0\x51\x0b\x74\xf2\x02\x3a\x3d\xff\xc6\xb8\xe1\x3b\xca\x0d\x2b\xb4\x99\x7c\xbf\x7d\xc7\xf9\x1f\xec\xb7\x4b\x88\xb4\x66\xc2\x68\x30\x18\x0d\x0e\xa3\xae\x22\x50\x37\x30\xac\xa9\x05\x6a\x45\x18\x36\x19\xf4\x26\x87\xde\x50\x31\x6c\x68\x18\xd6\x2d\x18\xb6\x18\x8c\x16\x87\xd1\x54\x11\x68\x1a\x18\x36\xd4\x02\x8d\x22\x0c\xdb\x0c\x7a\x9b\x43\x6f\xa9\x18\xb6\x34\x0c\x9b\x16\x0c\x3b\x0c\x46\x87\xc3\x68\xab\x08\xb4\x0d\x0c\x5b\x6a\x81\x56\x11\x86\x6b\x0c\xfa\xda\x99\x42\x22\x02\xc3\x8e\x86\x61\x5b\xc1\xb0\x54\xe2\x8f\x94\x27\x9d\x10\xba\xd6\x12\x69\x27\xe6\x5d\x77\x51\x58\x19\xbe\xce\xe4\x7b\x27\x59\x93\xca\x43\x29\x28\x69\x1c\xe8\x6d\x91\x79\x7f\x35\x1d\xfb\x04\x9b\xeb\x0c\x39\xc1\xb1\x38\x33\x95\xbc\x65\x1b\x44\x71\x71\x55\xa4\xd4\x55\x93\x77\xc8\x25\xab\x45\x77\x50\x72\xc1\xd2\xc6\xc8\x9e\x7a\x37\xd2\x6d\xb7\xbc\xfc\x52\xa4\xdb\xee\x78\xec\xae\xa4\xdb\xa9\xdf\x9e\x79\x6b\x7f\xef\x48\x84\x4f\xf7\x55\x4f\xf7\x55\x8f\x76\x5f\xa5\x2d\xf1\xfc\x3e\x47\xbf\xc9\xf9\x7b\xdd\xe1\x3c\x54\x56\xb8\xf7\xe2\x68\xfe\x5e\x3d\x9a\xbf\xbf\xeb\xd1\xfc\xbd\x7a\x34\x7f\x5f\x74\x34\x9f\xa7\x60\x7e\xba\xa9\x7a\xba\xa9\x7a\xba\xa9\x52\xbe\x3c\xdd\x54\x3d\xdd\x54\x3d\xdd\x54\xe5\xcd\x3e\xdd\x54\xe9\x1f\x9f\x6e\xaa\x1c\x8f\x4f\x37\x55\x4f\x37\x55\x4f\x37\x55\xf0\xf7\x74\x53\x55\x4e\x89\xfb\x74\x53\xf5\x74\x53\xf5\x74\x53\x25\xfd\x3d\xdd\x54\x3d\xdd\x54\x3d\xdd\x54\x3d\xdd\x54\xfd\x4f\xbe\xa9\x7a\xb0\x3b\xaa\xbb\xdd\x4e\x95\xb9\x97\x2a\x71\x23\xf5\x58\x77\x51\x7f\xef\x7c\x28\x4f\x77\x51\xff\xfc\xbb\x28\xf9\xee\xa8\xd7\x9a\xeb\xe8\x24\xdf\x1c\xf5\x5a\xd2\xb5\x11\x3c\x3c\xfe\x9d\x11\xf5\xd2\x14\xb7\x46\xf6\xa0\x02\xdc\x43\xbb\xe8\x5a\x09\xdc\x38\x65\x8f\x62\x29\x66\xba\xa9\xaf\x88\xc2\x0c\xa5\xfd\xf8\xda\x84\x73\x2c\xd0\x39\x96\xaf\xe9\xf8\x9f\x4d\x9a\x6c\xb4\x3b\xee\x43\x39\x3b\x74\x87\xf3\xd5\xb8\xef\xf1\x8d\x4d\x8f\xab\xb6\xe8\x71\xff\xf1\xb9\x0d\xb3\x41\x21\x43\xc0\xa3\x4a\x84\xe8\x5f\xf2\x38\x39\x54\x87\xac\x12\xd9\xda\xf8\xd8\x9f\x2a\x80\xcc\x48\x68\xca\x67\x23\x28\x9a\xed\xec\x4f\x7a\x51\xf9\x03\x2d\xd1\xf1\x59\xe2\x8d\x56\xd1\xbf\xa0\x57\x8e\x58\x0a\x57\xfe\xd4\x8e\x33\xec\x1b\xa6\x86\x40\x9a\x80\x63\xbb\x63\x3c\x79\x4d\x66\x7c\xfe\xf4\xf4\xac\x2a\x7e\x96\x55\x43\x10\xcd\x1f\x2c\xcb\xac\x00\x74\x6f\xb5\x1c\xd7\x84\x80\x16\xc4\xc8\xbf\x4e\xa6\xc7\xae\x32\x54\x5a\x16\x4e\xce\x8d\x76\xc7\xa1\x10\xa9\x39\x95\x21\xd6\x46\xcb\x2a\x46\xa4\xf5\xa4\x29\x46\xf2\x41\x0b\xb5\x2f\x7f\xe4\xc3\x39\x37\x03\x3c\x28\x07\xd5\xea\x9f\x65\x3c\xb5\xf9\x10\xab\x29\xa2\xcb\x28\xa2\x2a\xb5\xc8\xb2\x88\x42\xd0\xa0\xd3\x84\x71\x8c\x2a\x95\xef\x0a\x09\x3b\x08\xd7\x4a\xb4\x05\x04\xeb\x26\xd6\x9c\x50\xd5\xf7\x6a\x67\xbf\x92\xba\x15\xb6\xa6\x48\x15\x86\xd7\x59\x9e\xd7\x20\xd2\xf3\x18\x68\xc7\xa7\x4f\x10\x07\xc5\x72\xa3\x95\x93\x7a\x68\x9c\xdd\xc9\x58\x28\x73\xc5\xc4\x32\x05\xbb\xef\x55\xee\xed\xb5\x1e\x42\xe8\xed\xb5\x16\x96\x78\xcd\x3d\x56\x13\x77\x7b\x2d\x6b\x6c\x0b\xb8\xa1\x09\x71\x70\x87\x1d\x7e\x2b\x89\xa7\xca\x2e\xcf\x5e\xc0\x20\x7c\x83\xa8\x78\x01\x69\x4e\x0d\x34\xa7\xe9\xf9\xc9\xc4\x93\x52\x22\xd4\x1c\xaa\xbf\x6a\xc8\x60\xf5\x58\x73\x04\x75\x29\xea\x97\xb6\x8a\x09\xa8\xae\x0a\x42\x8d\x18\x57\x4a\x88\x21\x6d\xf0\x82\xc5\x77\x18\x64\x3c\x0b\x36\x70\x61\xf8\x42\xf0\x22\xbb\xf8\xcf\xb0\x99\x2f\x2f\x5b\xf7\xf0\x05\xd8\x3d\x9a\x93\x00\xe9\x3b\x5a\x6d\x64\x88\x1e\x66\xc5\x01\xa4\xc5\x57\x1d\xa3\xf9\xe2\x95\x47\x0a\x15\x9f\x34\x7b\xad\xc7\x3a\x66\xde\x2f\x5d\xdf\xb7\x3c\x5f\x3e\xda\x29\xf0\xdb\x06\x71\x26\xac\x0a\xa7\x38\xb9\xc4\xcf\x9f\x55\x06\x55\xd4\xa8\xd5\x1b\xa8\x7f\x83\x7a\xff\xdf\xff\x1b\x24\xe1\x00\xed\xe3\x34\x0a\xc7\x2b\x68\x73\x3c\x46\x49\x78\x31\xca\x52\xc4\xca\x07\x2b\xcf\x9f\x3f\x3b\xc2\x41\x98\x66\x49\xd8\x9f\x01\x7c\x3f\x0a\x20\x28\x4f\x18\xa1\x34\x9e\x25\x03\x0c\x6f\xfa\x61\xe4\x27\x37\x84\x1d\x4c\x52\x8f\x45\x69\x48\xe0\xbf\xf1\x2c\x43\x13\xe0\xe9\x03\xe0\xac\x1e\xf2\x13\x8c\xa6\x38\x99\x84\x59\x86\x03\x34\x4d\xe2\xcb\x30\xc0\x01\x0d\x3a\x41\xd6\xe9\x30\x1e\x8f\xe3\xab\x30\xba\x40\x83\x38\x0a\x42\xba\x86\x49\xa5\x09\xce\xba\x6c\xc5\x2f\x23\x15\xad\x14\x14\xc3\x14\x9f\x41\x1c\x60\x34\x99\xa5\x19\xd9\xa8\xfd\x30\x02\xa0\x7e\x3f\xbe\x24\x9f\xa6\x37\xd0\x45\x14\xc5\x59\x38\xc0\x1e\x8d\x2b\x34\x0e\x53\xd0\x2c\xcb\xed\x45\x81\x86\x4c\x10\xa6\x83\xb1\x1f\x4e\x70\xb2\xe2\xc2\x21\x8c\xe4\x81\xe0\x38\x4c\x93\x38\x98\x0d\xf0\x83\xa3\x81\x58\xd7\x82\x78\x30\x13\x71\x30\x48\x8d\xd5\x38\x61\x31\x32\x26\x7e\x86\x93\xd0\x1f\xa7\xf9\x30\xc3\xdc\x40\x35\x09\x75\x32\xcf\x27\xbb\x7b\xc7\xe8\xf8\x60\xe7\xe4\xd7\xcd\xa3\x6d\xb4\x77\x8c\x0e\x8f\x0e\x7e\xd9\xdb\xda\xde\x42\x6f\xff\x8d\x4e\x76\xb7\x51\xef\xe0\xf0\xdf\x47\x7b\xef\x76\x4f\xd0\xee\xc1\x87\xad\xed\xa3\x63\xb4\xf9\x71\x0b\xf5\x0e\x3e\x9e\x1c\xed\xbd\xfd\x74\x72\x70\x74\x8c\x7e\xdc\x3c\x46\x7b\xc7\x3f\xc2\x87\xcd\x8f\xff\x46\xdb\xbf\x1d\x1e\x6d\x1f\x1f\xa3\x83\x23\xb4\xb7\x7f\xf8\x61\x6f\x7b\x0b\xfd\xba\x79\x74\xb4\xf9\xf1\x64\x6f\xfb\xd8\x43\x7b\x1f\x7b\x1f\x3e\x6d\xed\x7d\x7c\xe7\xa1\xb7\x9f\x4e\xd0\xc7\x83\x13\xf4\x61\x6f\x7f\xef\x64\x7b\x0b\x9d\x1c\x78\xd0\xa8\x59\x0d\x1d\xec\xa0\xfd\xed\xa3\xde\xee\xe6\xc7\x93\xcd\xb7\x7b\x1f\xf6\x4e\xfe\x0d\xed\xed\xec\x9d\x7c\x24\x6d\xed\x1c\x1c\xa1\x4d\x74\xb8\x79\x74\xb2\xd7\xfb\xf4\x61\xf3\x08\x1d\x7e\x3a\x3a\x3c\x38\xde\x46\xa4\x5b\x5b\x7b\xc7\xbd\x0f\x9b\x7b\xfb\xdb\x5b\x2b\x68\xef\x23\xfa\x78\x80\xb6\x7f\xd9\xfe\x78\x82\x8e\x77\x37\x3f\x7c\xb0\xf6\x92\xe0\xae\xf4\xf1\xed\x36\xfa\xb0\xb7\xf9\xf6\xc3\x36\x6d\xe9\xe3\xbf\xd1\xd6\xde\xd1\x76\xef\x84\x74\x27\xff\xd5\xdb\xdb\xda\xfe\x78\xb2\xf9\xc1\x43\xc7\x87\xdb\xbd\x3d\xf2\x63\xfb\xb7\xed\xfd\xc3\x0f\x9b\x47\xff\xf6\x18\xcc\xe3\xed\xff\xfd\x69\xfb\xe3\xc9\xde\xe6\x07\xb4\xb5\xb9\xbf\xf9\x6e\xfb\x18\x55\xe6\x0c\xc9\xe1\xd1\x41\xef\xd3\xd1\xf6\x3e\xc1\xf9\x60\x07\x1d\x7f\x7a\x7b\x7c\xb2\x77\xf2\xe9\x64\x1b\xbd\x3b\x38\xd8\x82\x81\x3e\xde\x3e\xfa\x65\xaf\xb7\x7d\xfc\x1a\x7d\x38\x38\x86\xd1\xfa\x74\xbc\xed\xa1\xad\xcd\x93\x4d\x68\xf8\xf0\xe8\x60\x67\xef\xe4\xf8\x35\xf9\xfd\xf6\xd3\xf1\x1e\x0c\xda\xde\xc7\x93\xed\xa3\xa3\x4f\x87\x27\x7b\x07\x1f\xab\x68\xf7\xe0\xd7\xed\x5f\xb6\x8f\x50\x6f\xf3\xd3\xf1\xf6\x16\x8c\xee\xc1\x47\xe8\xea\xc9\xee\xf6\xc1\xd1\xbf\x09\x50\x32\x06\x30\xf8\x1e\xfa\x75\x77\xfb\x64\x77\xfb\x88\x0c\x28\x8c\xd4\x26\x19\x82\xe3\x93\xa3\xbd\xde\x89\x5c\xec\xe0\x08\x9d\x1c\x1c\x9d\x48\x7d\x44\x1f\xb7\xdf\x7d\xd8\x7b\xb7\xfd\xb1\xb7\x4d\xbe\x1e\x10\x28\xbf\xee\x1d\x6f\x57\xd1\xe6\xd1\xde\x31\x29\xb0\x47\x9b\xfd\x75\xf3\xdf\xe8\xe0\x13\x74\x99\xcc\xd1\xa7\xe3\x6d\xfa\x53\xa2\x58\x0f\x66\x12\xed\xed\xa0\xcd\xad\x5f\xf6\x08\xda\xac\xf0\xe1\xc1\xf1\xf1\x1e\xa3\x13\x18\xb2\xde\x2e\x1b\xee\x95\xe7\xcf\x5e\xae\xaa\x3a\xaf\x7d\x3f\x1b\x3d\xac\xde\xab\x5c\xd4\x69\x1a\xf8\x58\x14\xa1\x8f\xa5\xac\xb3\xe1\xc2\xce\x8f\xb2\x14\x65\x7e\x9f\x4b\x2c\xa4\xca\xf9\x97\xb1\x35\xd8\x66\x2e\x47\xd5\x3c\x84\xea\x1e\x42\x0d\x0f\xa1\xa6\x87\x50\xcb\x43\xa8\xed\x21\xd4\xf1\x10\x5a\xf3\x10\x5a\xf7\x10\x7a\xe5\xa1\x7a\xcd\x43\xf5\xba\x87\xea\x0d\x0f\xd5\x9b\x1e\xaa\xb7\x3c\x54\x6f\x4b\x16\x96\x6b\xb4\x2e\xf9\x46\xe0\x91\xf2\x04\x46\xbd\x4d\xe1\x92\x7a\xd0\xd6\x2b\x06\xbf\xc1\x60\xd4\xa1\x8d\x1c\x4e\x93\xb5\xd5\x62\xb8\xbc\x62\x30\xd6\x25\x3c\xd7\x18\xac\x0e\xc3\xa5\x4e\x61\xd6\xe5\x58\xcb\x75\x56\x97\xe3\x52\xa3\x30\x00\x0f\x8e\x67\x93\xc2\x22\xf0\xeb\x72\xbf\x65\x38\x2d\x56\xb7\xcd\x70\x5f\x63\x30\x1a\x12\x9e\x75\x06\x6b\x9d\xe1\xc2\xfa\x5d\x6f\x9e\x55\x5f\xcb\x73\x91\xcc\x99\x0b\x8e\xc7\x9a\x34\x56\x0d\x06\x93\xe3\xdc\x51\xc7\x03\xfa\xd6\xd4\xfa\xde\x61\x75\x9a\x39\x2c\xa8\xdb\xce\x71\xe6\x30\xf8\x78\x40\x5b\x75\xad\xef\x50\xa8\x2d\x75\x70\x8d\x21\xd8\xc9\x07\x57\x00\x69\x48\x03\x4d\x91\xcd\x01\xad\xb3\x3a\xd2\x60\xc1\xc4\xb4\xf3\xc1\x15\x30\x9a\xd2\x40\x53\x64\x25\x84\x1a\x6c\x64\x6b\x12\x30\x3e\x1a\x6b\x62\xf6\x04\x85\x22\x36\x3a\x14\x59\x75\x36\xd2\x79\x2b\x83\xa2\xc8\xc6\x0a\xd0\x93\x5b\xe2\xb4\xd5\x94\xc6\xb3\x93\x7f\x53\x68\x7a\xcd\x83\x4f\x30\x54\x9c\x5e\x5f\xe5\xb4\xc7\x69\xaa\xde\x96\x86\x75\x8d\x95\x55\xe6\xa3\x9e\x13\x81\x98\x8b\x57\xac\x20\x27\x9e\x75\xa9\x0c\x47\x7c\x0d\x7e\xcb\x67\x29\xb1\x96\x5b\x79\x55\xde\xbe\x58\xf3\xf2\x9a\x58\x57\x40\xe6\xa0\xf8\xfa\x6c\xe7\xb4\x2f\xfa\xd9\xc8\x51\x10\xe3\xc4\x48\x86\xc2\x45\xda\x94\xcc\x5b\x20\x0c\x31\x65\xf0\xdb\x39\x02\xd0\xcf\xb5\x7c\x21\x42\x83\x2d\x86\x48\x47\x43\xba\xa9\x0e\xbe\xe8\x74\x3d\x87\x23\xc6\x4e\x2c\x68\xf8\xae\xc0\x11\x0c\xa4\x2e\x0d\x52\x27\x6f\x57\x2c\x3c\xb6\x80\xeb\x4d\xcb\x7c\x88\x0e\x68\x88\x73\x40\x62\xc1\x35\xa4\xff\xb6\xc5\x2a\x56\x07\xa8\x6d\x29\xd7\x52\x67\x46\xcc\x64\xde\x29\x54\xaf\xa3\x33\x25\x4b\xf6\xf9\x88\xac\x10\xcb\x7c\x20\x11\xaa\xb9\xe6\xa1\xda\x75\x7b\x73\xbd\xb1\xf6\xea\xd5\x2b\xf2\xbb\xb3\xbd\xf5\x6a\xfb\xed\x66\x9d\xfc\x5e\xdf\xa9\xbf\x7d\xdb\xdb\xea\x91\xdf\x9b\xaf\xda\xcd\x9d\xad\xd6\xb6\x3a\xdf\xa3\xc4\xd9\x40\xbb\xb6\xd9\x58\x7f\xbb\xdd\x81\x06\x7a\xad\xad\xad\x7a\xa3\x05\x0d\x6c\xad\xd5\x9a\xdb\x3b\x4d\xf2\x7b\x6d\xb3\xb3\xb5\xd6\xd9\x86\x86\x39\x42\x67\x56\x7d\xc0\xd1\xde\xe1\xf6\xfe\x56\xbd\x53\x83\xf0\xfb\x73\x74\x48\xa2\x6c\xae\x45\x92\x5e\xd1\x5d\xf9\xae\x77\x45\x54\x99\x08\x48\x38\x82\x60\x77\xd6\x5a\xed\x46\xb3\x06\x23\xb8\xbd\xd3\xdb\xda\x7c\xbb\x0e\x1d\x7c\xb5\xfe\x76\x73\xab\xb7\xb3\x4d\x7e\xd7\x6b\xcd\x46\xbb\xb5\x06\x83\xd3\x6b\x6e\x35\xb6\xeb\x3b\xb5\x33\xa7\x6a\xbc\xac\x52\xde\xaa\xd8\x2d\xed\xa5\x54\x2f\xb8\xa9\x99\x6f\x8e\x4f\xb1\x00\xdd\x6b\x6e\x16\xe9\xb8\xbe\xd9\x3f\x97\x4a\xf3\xcb\x83\x73\xd3\x90\x09\x15\xdd\xa9\x48\xf5\xd0\x06\xaa\x98\x05\x10\x35\x00\x95\x1a\xcb\x0d\x1f\xa4\x97\x8b\x19\x95\x1a\x00\x99\x5d\xa9\x06\xd0\xb4\x2e\x35\xc1\x15\xa8\xc6\xd0\x3c\x5b\xe7\x5d\x24\xee\x1f\x08\x29\x3a\xaf\x1c\x81\x01\x9c\x8f\xc6\xee\x02\x09\x14\x48\x9c\x05\x40\xfc\x3c\xff\xe2\x86\x00\x32\xd1\xf9\x17\x37\x04\xd8\xa6\xcf\x53\x37\x04\xd8\x34\xce\xd3\xc4\x1e\xd1\x7a\x75\x95\xac\xb2\xcf\xe4\xd0\x7c\xe9\x27\x21\x91\x8e\x2d\x97\xb4\xfe\xd8\x43\xfd\xb1\x87\x06\x63\x0f\x05\x63\x0f\xe1\xb1\xa5\x21\x3f\xf1\x50\x3f\xf1\xd0\x20\xf1\x50\x90\x78\x08\x27\x7a\x63\x3e\x41\xc5\x27\x08\xef\x9a\x2e\x23\xfd\x04\x82\x8e\xc3\xc7\xba\xfe\x71\x40\x3e\x0e\xe8\xc7\x86\xfe\x31\x20\x1f\x03\xfa\xb1\xa9\x7f\x84\x03\x03\xa6\x1f\x5b\xfa\x47\x91\xa6\xda\x57\xf3\x52\xf3\x2e\xe9\xb7\x82\x56\x53\x42\xf8\xef\xd2\x06\xaa\x5b\xd7\x76\x46\x96\x8f\x3f\x46\x4b\xf9\x9a\x5a\xfa\x32\x3e\x0d\xcf\xce\xaa\x5f\x6d\x4e\x0c\xe0\xb5\xf3\xa6\xde\xa9\xfe\xf9\xfc\x99\xca\x1a\x49\x1b\x68\x58\xaf\xf4\xc7\xde\x60\xec\x05\xe3\x2a\x5a\x42\xa3\xb1\xdd\xf7\xe6\x16\x09\x85\x5c\xf8\xa6\xd9\xa0\xaa\x36\x0b\xb4\x86\x0e\xcd\x18\x79\x03\x5a\x6b\xdd\x09\xad\xa9\x43\x33\xa6\xca\x80\xd6\x69\x39\xa1\xb5\x74\x68\xc6\xdc\x4a\xd0\xfe\x5c\x5d\x65\x10\xd7\x6b\x4e\x88\x6d\x1d\xa2\x41\x10\xc8\x1e\x26\x9d\x4c\x62\x66\x9d\x2e\xf2\x05\x25\x71\x36\xae\x64\x5e\x4a\xa6\xd5\xe6\xb4\x01\x34\x90\x2d\xe1\xb1\x7d\xca\x61\x45\x18\x4b\x8a\xfc\x01\xdd\x06\xb6\x2f\x40\xee\xd0\x2e\x59\x93\x75\xab\x1b\x10\xac\x97\xbe\xad\x36\x2c\x33\xe3\x26\x51\xa0\xea\x27\x68\x49\xa2\xd6\xe4\xee\xd4\xda\xae\xf4\x13\x6f\x90\x78\x41\x02\x23\x9e\xdc\x8f\x5a\x5b\x3a\xb4\xfb\x52\xab\x0a\xed\x5e\xd4\xda\xd0\xa1\xdd\x9b\x5a\xeb\x3a\xc4\x07\xa6\xd6\x04\x6e\xad\x0b\xc8\x35\x71\x90\x2b\x70\xd4\xc4\x46\xae\xc0\x88\x6d\x5f\x80\x45\x53\x72\x4d\x9c\xe4\x0a\x1b\x80\xad\x36\x6c\x0d\xa6\x85\x86\xce\xca\xf7\xe4\x74\x0c\x20\x43\x82\xd5\xaf\x26\x61\x92\x7f\x36\x50\x65\x97\x9a\xe6\x0e\x08\x67\x0e\x2c\x3d\xdd\x65\x26\xbc\xbb\xd4\xfc\x36\x20\xe5\x6c\x23\xb2\xcb\xcc\x74\x77\xa9\x21\x2d\x26\xe5\x7c\x6b\xb9\x26\x2b\x07\xc6\xb2\xb0\x23\xf4\xad\xe5\x5a\xac\x1c\x18\x26\xf7\x49\xb9\x81\xb5\x1c\x18\x30\x2b\xc3\xa2\x8b\xb5\x3b\x2c\xb5\xc6\x3d\xcc\xb3\x02\x3f\xf3\x85\x30\x44\x1e\x2c\x1b\xff\xfc\x34\x8c\xbc\x64\xf4\x36\xcc\xd2\x93\x38\x03\x8e\x47\x61\x46\x5b\x7e\xe6\x53\xab\xad\x97\x68\xdd\x02\x1d\xea\x7c\xc0\xc3\xcc\x48\xda\x08\xe5\x8d\xce\x6c\x06\x81\x99\x85\x18\xb1\x7c\x8b\xd4\x98\x29\x07\x49\xa4\xc9\xf6\x19\xfa\xba\x41\x13\x0b\xe7\x36\x12\xa2\xc4\xbf\x50\xb3\xa1\x53\x6b\x0e\xa9\x52\xa9\xe4\x45\x97\x10\xe1\x0f\x04\xe4\xab\x2a\x01\xd5\x22\xeb\xb6\xde\x72\x08\xd0\xbc\x2a\x1d\x8e\x5c\x78\x96\x5e\x96\x17\x9e\x0d\x60\x4c\x70\xd6\x80\xcd\x13\x9c\x6d\x1d\x95\xf3\x74\xe4\xf9\x30\x79\x8e\x1d\x30\x8e\xb1\xa4\xed\x58\x5d\x85\x93\x20\x82\xec\x2e\xd4\x21\xcb\x6a\x38\x35\xa5\x27\x2f\x33\x9b\x4b\x31\x59\xc2\xea\x96\x65\x74\x0b\xe1\xec\xa2\x0d\x24\x8b\xef\xf7\x3b\xbf\xb5\x4b\x1d\xdf\xec\x27\xb2\x5d\x38\x8a\xed\x5a\x9c\x49\x50\xd1\x19\x6c\x57\xb8\xeb\xed\x2a\xc7\xab\xdd\x85\xcf\x55\x94\x42\x76\x95\x33\xd5\xae\xf3\x30\x35\xdf\x14\xee\x88\xde\x84\xd3\xc9\x65\x19\x2c\x02\x18\x6c\xb5\x28\xbb\x31\xd7\x26\x48\x61\x53\x83\x71\x1c\x15\x33\x28\x30\x25\x20\xa5\x72\xed\x02\x3c\xba\xcd\x20\xe8\xe7\x73\x83\x48\x68\x3d\x93\xd6\x18\x9a\xf0\x55\xb1\x8b\x82\x9f\xb7\xf4\xf6\x1f\xc9\x16\x71\xc3\x7a\xe5\xda\x43\x37\x1e\xfa\x62\x4b\xf3\x51\xa9\x5c\x83\x67\xe7\x0d\xfc\xfb\x25\xcf\xd6\x7e\x6b\xc0\x69\x14\xc3\xa9\x5c\x57\x7f\xaa\xdc\x54\xa9\x3b\xf9\xff\x25\x0f\x5f\xaa\xd5\xea\xff\xcf\xde\xf7\x2f\xc7\x6d\x23\x0d\xfe\x1d\x3f\x05\x76\xab\xd6\x1e\x45\x23\x89\x00\x7f\x81\xb6\x95\xbb\x44\xb1\x3f\xe7\x62\xc7\x2e\x5b\x7b\xf1\x57\x2e\x7b\x17\x24\x41\x0d\xe3\xd1\x8c\xbe\x19\xca\x1a\xed\xc6\x5b\xf7\x1a\xf7\x7a\xf7\x24\x57\x68\x80\x24\x48\x02\xe0\x8c\x2c\xe7\xdb\xec\x5a\x5b\xeb\xcc\x0c\x1b\xdd\x8d\xfe\x85\x26\x7e\x34\x1e\xd8\xb0\xf9\xa3\xd8\x04\xa2\x7f\x08\x8c\x2d\x6b\x16\x5c\xc1\x38\xae\xbb\x80\x01\x78\xbb\xde\xbb\x3b\xf9\x07\x30\x67\xc7\x18\x6e\x23\x33\x21\xb4\x5f\x5b\x54\x16\x5c\x90\x4a\x6c\xa6\x0b\x23\xa6\xcd\xc3\x87\x0b\xe0\x6a\xf3\xcd\x37\xdf\x4c\x7c\x72\xb0\xd0\x99\x92\x1f\x9c\xbb\x61\xea\xcd\x30\xf2\x1e\xb8\xed\x36\xc3\x58\x6f\xfb\x51\xfb\x5b\x60\xcf\x53\xfd\xb9\x5a\xca\xc8\x34\x44\x63\xb9\x9f\xc7\x02\x7d\xd3\x8b\x79\x94\x67\xb4\x3b\x59\xea\x09\xbc\xc9\x3d\xc5\xe2\x3d\xc3\x2e\x1c\x7b\xab\xab\x9a\x5b\xd3\x76\x9b\xe1\xe4\x60\x6f\xab\x4d\x0d\xb0\xdd\x56\xa5\x5a\x39\x4f\x9e\x7d\x7b\xf2\x1b\xa8\xc6\xd1\xfc\x3d\xbf\x86\xa6\x6b\x9e\xad\x78\x65\xb9\x3b\xc9\xa2\x50\xb8\x72\xf0\x16\x15\x2a\x2f\x32\x6c\x54\xf3\xe4\x9c\x65\xad\x7a\xf4\x2d\x56\x06\x0d\x75\x80\x87\x5a\x3a\x67\x99\x41\x53\x5f\x7d\x94\xeb\xc0\x96\xad\x51\x35\xa4\xf9\x76\xa2\x8f\x6f\xa7\x71\xfc\x65\x8b\xd3\xbf\xc2\x91\x95\xcf\xbd\x74\xdf\x2b\xac\xa6\x11\xb6\x96\x4c\x7b\xf5\xe4\xdb\x03\xbc\xc5\x4a\xc6\xf0\xae\xea\xdb\x5c\xbf\x38\x86\xd3\xa7\xed\x12\x46\xb9\x28\xab\x89\xa1\x00\x55\x77\x49\x83\x17\x59\xce\x52\x9a\x18\x6a\x33\x79\x9b\x84\xa6\x2c\xcf\x0a\xde\x59\xe3\x30\x01\x66\x7e\x4e\x38\x2e\xbc\xee\xb3\x4f\x5f\x02\xb1\x65\xe8\xe6\xe4\x7b\x38\x83\x3e\x40\xb0\xcd\xdc\xb3\x79\xba\x58\x3c\x4a\xcd\x93\xc5\x90\x30\x9a\xa7\x8a\xe1\x75\xd5\x3c\x51\x2c\x1e\xf1\x66\x9a\x78\xc0\xa9\x75\x9e\xd8\x3a\x27\x6c\x79\x5b\x80\x79\x1f\x24\x4f\x98\x5a\x6a\xc1\xfc\x2c\x13\xff\x6e\x09\x8c\xee\xd9\xd3\xfa\xaf\x9e\x50\x32\x23\xaa\xcf\x39\xfc\xfc\xa6\x44\x07\xc8\x7f\x8b\xde\xa9\x8f\xb4\xfd\x88\x03\xed\x73\x64\xbb\x3b\x52\xb1\x34\x59\xc0\xe1\x58\xf9\x6e\x09\xaf\x0f\x3e\x36\x97\xa9\x31\xbf\x09\xc1\xd4\xd2\x84\x09\x24\x21\x20\x61\xf2\x4d\x26\x86\x03\xb2\x1c\xed\x03\x21\xdb\x44\x23\x7a\x88\x88\x67\x95\x1a\x4c\x9b\x4d\x26\x29\xba\x8b\x32\x99\xe7\x8a\x8f\x39\x60\xf6\x36\x21\x93\xab\xb0\x23\x53\x7c\xe8\x21\x0a\xc6\x48\xa4\xe8\x1d\xca\xd0\x3b\x94\x4b\xcc\x11\xcf\x13\x9e\x32\x53\xd1\xa1\x1e\xe6\x68\x07\xe6\x25\xef\xe2\x53\xa6\x7a\x71\x80\xbc\x4d\xec\xf1\x20\xf0\x49\x60\xa7\x75\xf4\x75\x43\x8e\x7a\x7b\xe8\xeb\xa3\xad\xfb\x22\xf0\xfb\x61\x92\xfb\x9c\xf4\x67\x79\x90\x45\xa5\xc2\x5f\x72\xd3\x74\x1f\x3a\x46\x99\x69\x8a\x0f\x01\xc9\x87\x0f\x91\xef\xa9\x5e\x82\xfa\x8d\x77\x8b\xa2\x63\x64\xe2\x83\x6d\x77\x5a\x6b\xab\xc9\x40\x35\x89\x56\x4f\xb6\xb1\xfe\x09\x6f\xd4\x99\x08\x84\x09\xc3\x41\xe5\x13\xd4\x99\x04\x84\xc9\xc2\xcc\x0c\xe3\xeb\x13\x85\xb9\x19\x26\xd0\x27\x09\x79\x1f\xe6\xcb\x04\xdf\x3f\xeb\x04\x9f\xc8\x85\x0f\x8b\xf9\x72\xb9\xd2\xe7\xdc\x8e\x60\xa0\x56\x7f\x9f\x44\x04\x6a\x21\xb4\x98\x47\xe6\xe9\x06\xd3\x74\x9f\x69\x86\x6e\xc7\x79\x20\xe3\x74\xdd\xef\x71\x36\xe8\xcb\x14\xc2\x60\x32\x40\xa4\xcf\x3b\xcd\x1e\x40\x03\xd7\xc4\x41\x37\x21\xef\xce\x19\x88\x67\x5f\xa6\x0b\x6e\x75\xba\x00\xf4\xb1\xc5\x4c\x81\x59\x2d\xed\x24\x81\x52\x8d\xfd\xd8\x94\x00\xb0\x4f\x0b\xd0\xdf\x75\x81\x8d\xf5\x8c\x91\x30\xfa\xdc\xb5\x31\x14\x95\x7f\x9f\xe9\x83\xc1\xf4\x80\xfe\x0e\x4f\xc2\xa8\xf3\x16\xaf\x9d\xc2\xee\xcf\x0a\x10\x12\x6c\x37\x2f\x20\x00\x3b\x38\xe1\xbb\x44\xfe\x9b\xce\x0d\x64\xd8\x0b\x13\x9e\x53\xf1\xca\xef\x47\x71\x96\x87\x5e\x0c\x9f\xbd\xd8\xcb\x73\x0c\x9f\x8b\xd8\xe3\x61\xe2\x9b\xe7\x0c\x8a\x22\xf3\xbc\xd4\x87\xc9\x85\x88\x86\x14\x87\x58\x7e\x0e\x8a\x84\x16\x0c\x10\xa4\xbc\x60\x41\xc1\x82\x1d\xa6\x0b\xb6\xca\x3c\xb5\xb0\xaf\x44\xa7\xb5\x74\x9c\xa2\x85\x88\xda\xa4\x33\x07\xc7\xc3\xe4\xc5\xb2\xb0\xf4\x65\x88\x1e\x19\x71\x09\x09\x76\x1d\xa4\x45\x93\x91\x61\xba\xe3\x1d\x83\x81\x9a\x10\xf3\x21\xf6\x2f\x43\xf5\x27\x0c\xd5\x42\x2b\xdb\x0d\xd6\x46\xe5\x74\x86\x6b\xa9\x20\xe7\x80\x4d\x48\xff\xa8\xb3\x76\xae\x59\x0d\x47\xf7\xe3\x44\x0c\xe0\xc9\x97\x79\xfd\xff\x9e\x81\xf9\xf7\x77\x2c\xef\x07\x79\x89\x43\xf9\xb7\xe6\x54\x2e\x5a\x2d\x2f\x17\x39\xca\xba\xe7\xf5\xb4\x1e\x3c\xe9\x5f\x9d\xf2\x63\x77\x19\xa0\x9e\xa8\xe5\x2d\x0e\xf9\xc4\x94\xc1\x20\x7d\x49\xb9\x5c\xbf\x58\x95\xe7\x7c\xb2\x30\x0e\x63\xeb\xff\x5a\x55\x3f\xd5\xef\xf9\xe2\xcb\x64\xd1\x7f\xcf\x6c\x26\x82\xa5\x3a\xd1\x31\x22\x0f\xea\xcf\x0f\x8f\x25\x86\xfa\x07\xc7\xdc\xf0\x1f\x26\x0b\xf4\x27\x05\xb6\x67\x9d\x2f\x54\x3e\x5a\xb0\xf9\x9a\x8f\xef\x0a\xec\xcf\x8f\xd5\xef\xe3\xab\xcb\xee\x1b\xae\x41\x2c\x67\xbc\x7a\xbc\x62\xf0\x99\xcd\xbf\x2b\xab\xb5\x41\x40\xcd\x12\xfe\x02\x1d\xa0\xc9\x02\x2a\x7b\xee\xa1\xaf\x3b\x93\x1f\xfd\x99\x2c\x8d\x56\x3d\x4b\xad\x57\x66\x87\xdf\x40\x21\xbd\xfa\x3d\x57\xb3\x72\xce\xd1\x44\x3d\x7b\x88\xd4\x96\xcc\xbe\x14\x5b\x6d\x5a\x05\xdd\xa0\xa0\x56\x29\x3f\x79\x23\x81\xa0\xec\xe8\x40\x10\x60\x0b\x17\xcb\xab\xc9\x62\x8a\x30\x3a\x42\x64\x6f\x8b\x8a\xed\x08\x6e\x42\xd9\x05\xad\xbf\x67\x2c\x9e\x2d\x51\xec\xef\x8f\x4c\x85\x2e\x3a\x10\x75\x86\x34\x69\x71\xde\x7c\x8d\x4d\x24\xde\xdb\x65\xd3\xc3\x0c\xfd\xb3\xaf\xb4\x3d\x39\x5c\xcf\xcb\x8c\x4f\xbc\xbd\x2f\xab\x5e\x5b\xaf\x7a\x0d\x1e\x15\xf0\x28\x34\x3d\x3a\x83\x47\x83\x05\x23\xc8\x59\xe0\x51\xfc\xc9\xcb\x68\x91\xa3\xd6\xfd\x6f\xbd\x8c\x76\xc6\xce\xcf\x99\xb7\x69\x16\xd3\xf0\x40\x28\x43\x68\xd8\x68\x3c\xa9\x5b\x3e\x7c\x88\x88\x5c\xf4\xaa\x7f\xf9\xe6\x9b\x6f\x50\xbc\xb7\x87\xd0\x3b\x33\xa6\xee\x5f\x07\x13\x0e\x06\x98\x30\xdd\xdb\xdb\x0e\x53\xb7\x9d\x6f\x0c\x2f\x9d\x9e\xe0\xb6\xdf\xc6\x4d\xf2\x5d\x60\xad\xdb\x58\x32\xab\x75\x1b\x6f\xea\x7a\xd3\x5b\x32\xdb\xc5\xe4\x0f\x31\x25\x3b\x76\xbb\x6e\x67\xbe\x93\x00\xb5\x86\xa3\x94\xb8\xaf\x7a\x0e\x45\x7e\x55\x0f\xf7\x9d\x0b\xa6\xb6\xd5\xcf\x0c\x4e\x35\x4e\x38\xba\x8b\x0a\xd8\xec\xf6\x0f\xf1\xf1\xcc\x76\x85\xcb\x39\x83\x0a\x73\x0c\xdd\x45\x29\x80\x33\xb9\x3a\xf8\x0e\xa9\x75\x42\x13\xff\x90\xac\x94\x67\x82\xf1\x66\xa9\x55\x2d\xb6\xa9\xb5\x56\xb9\xf5\x4f\x3e\xc1\x89\xf6\x04\xfb\x9d\x47\x9d\x46\xe6\xb1\xad\x21\x06\xf7\xd4\x4c\x38\xd8\xb8\xac\x9c\xcc\xa1\x5d\xa4\x30\xca\x27\x58\x7b\x82\xb1\xfe\x28\x96\x3b\x5b\xe5\x23\x12\x9a\x47\x3c\x58\x40\x16\x94\x66\x68\xbf\x26\xbb\x2f\x84\xba\x2f\x2f\x7a\xb3\x2e\x1e\x43\x43\x82\x8e\x6b\xc1\xec\x0b\xd1\x9a\x28\x88\xc0\x75\x66\x40\x20\x62\x5d\xbf\x4e\xbb\xf8\x13\xe1\xd1\x94\x7e\x41\xed\x4c\xb8\x2d\x01\x9b\x96\xf9\xd0\xc8\x12\x69\xbf\xda\x3a\x1a\x59\x0e\x9d\x54\x42\x10\x15\x31\xd1\xfa\x77\x59\x1a\x95\x30\xa1\x82\x81\x92\xe1\x85\x19\x26\x52\x30\x50\x12\xfc\xcc\x0c\x13\x2b\x18\xf0\xf9\xd9\x97\x65\xd8\x2f\xcb\xb0\x5f\x96\x61\x87\xd9\xe6\x97\x65\xd8\x7f\xca\x39\xde\x30\xda\x79\x8e\x37\x8c\x46\xe7\x78\xf5\x77\xb6\xe1\x1c\x6f\x18\x7d\x99\xe3\xbd\xf5\x39\xde\x30\xda\x76\x8e\xd7\xa4\x9c\xee\x1c\x2f\x28\xc8\xbd\x69\xbb\x59\x3b\x33\x2f\xcd\x52\xef\x77\xbd\x34\xbb\x89\x82\xdf\xe4\xe2\x82\x86\xce\x97\x59\xe0\xee\x2c\xf0\x26\x82\x35\xd5\xc3\x4d\x14\x68\xbf\xbf\x8e\x02\x55\xa5\x1b\x20\x0e\xb5\x3a\xd1\x3b\xd5\x74\xd3\xfa\xf7\xf2\xc9\xf3\xbf\x3c\x7f\xfc\xf8\xd5\xa3\xd3\x57\xfd\xd9\xe2\x17\x3f\xfc\xe5\x87\x9f\xbe\x7f\xf4\xfa\xd1\xf0\x56\xee\x97\xcf\xff\xfc\xd3\xf7\x7f\x39\x79\xfe\xd3\xab\xd3\x6f\x7f\x6a\x5a\x6a\xe4\xe4\xb4\xf2\xc9\x76\xd3\xca\x5a\x8b\xd5\x6c\x59\x17\x6d\xe9\xcd\x49\xd7\xa4\xc5\xdb\x35\x9e\xa2\x6b\x5b\xa9\xf2\x4a\x4e\x89\x54\xe8\x21\x22\xc1\x03\x54\x19\xa6\x44\xb4\x3e\xbf\xd9\xa0\x7d\x14\xa2\xaf\xd1\xb5\x3c\x3d\x58\xd5\x87\x34\xe1\x13\xd9\x83\x99\x4a\xf4\x27\x14\x0d\x72\x11\x48\x03\xf9\xd5\x6b\x74\x8c\xae\xd1\x9f\x50\x68\xca\x12\xf9\xd5\x7f\x0a\xac\x04\x7d\x8d\x04\x1d\x5f\xd0\xd9\x33\x00\x6f\xe4\xb4\xdc\xeb\xde\xcf\xd7\xf2\xe7\xff\xb4\x4c\x05\x6b\x62\xbb\x28\x51\x09\xd7\x09\x18\x84\xd6\x48\x66\x23\x25\xb3\x91\x07\x34\x37\x06\xc1\x34\xa0\x52\xba\xe8\x5a\x82\x5e\x5b\xa6\x95\x5a\x03\xe9\x8a\xf1\x1a\x2e\xf8\x19\xf6\x5a\xc8\xb5\xdf\xf5\x8f\xa3\x7d\xeb\xad\x72\x74\xad\xe1\xe9\xe3\x57\x2f\x05\xaf\x1b\x0f\x9b\x8c\x41\xbf\x77\xc2\x32\x3f\x26\xc0\x80\x44\x6d\xac\xcf\xd6\x57\x3d\xdb\x32\x82\x3d\xad\xc1\x2c\x22\x54\x37\x4f\xfc\x82\x1e\xa2\xf8\x01\xfa\xc5\x31\x33\x07\x7d\x80\xa3\xa9\xe6\xaa\x28\x35\xf9\xb4\xac\x5e\x2c\xd7\x50\xc7\x55\x58\x15\x5c\x96\xfb\xcb\x1e\x3a\x40\xa6\xdd\xd4\x35\x72\xbd\xd1\x43\xa4\xea\x45\x98\x80\xc5\xdf\xa0\x83\xef\x8e\x11\x90\xd1\xb0\x58\x68\x75\x77\x54\xeb\x54\xbf\x39\x06\xb2\xf6\xcd\xd5\x03\xca\xcf\x34\xca\x1d\x54\x07\x86\xf7\x9e\x86\x81\xed\xa6\x96\x34\xc3\x5a\xf0\x4d\x05\x06\x34\xa2\x16\x6a\xdf\x89\x7e\x74\x84\x5e\xac\xca\xf3\xb2\x2a\x3f\x70\x74\xb1\x9c\x5f\x2f\x96\xe7\x25\x9b\xa3\xe5\x07\xbe\x42\xff\xf1\x78\x42\xf6\xee\xa3\xcd\x3b\x8a\xf6\xd1\xe6\x5d\x04\xff\x86\xf0\x6f\x20\xc2\x8c\x19\xa5\xb2\x68\x49\x5e\x9e\x1f\x78\x87\xbc\x4d\xec\xd8\x32\x6f\x61\x4e\x61\x38\x36\xda\xc7\xc8\xa2\x57\x2f\xc0\xcb\x39\x3e\x35\xfc\xd4\x05\xc6\xfa\x3a\x9b\x0e\xec\x67\x6f\xd7\xd5\x94\x35\xf8\x4f\xc5\xcf\x2f\x96\x2b\xb6\xba\xee\xdc\x44\x27\x5c\xe0\x54\x1f\x88\xac\xab\x94\xc6\x5b\x67\xcc\xde\x7f\x6a\xec\xd9\x18\xdf\xbd\xb5\x1d\x7f\xbb\x95\x1d\xbf\xb3\xae\xe3\xbb\x56\x75\x6e\xff\x2a\x81\xe5\x65\x75\x71\x59\x3d\x85\x57\xeb\x0e\x2c\x82\x24\x3d\xe7\xeb\x72\xc5\x73\xed\xa2\x81\xb4\xac\xd6\x75\x41\x68\xd9\xb8\xf3\xb6\x50\x37\x7e\xbe\x98\xd7\x6a\xd2\x6a\x70\xb3\x15\xbf\x8f\x08\x09\xa6\x88\x84\xd1\x14\xf9\x34\x98\xa2\x10\x93\x7e\x63\x75\x67\xc1\x7d\xf1\x4c\x7f\xd4\xbf\xb4\xa0\x7e\x69\xb6\xde\x5b\xa0\xf7\xae\x87\xed\x06\xf7\x17\xc0\x4c\x2d\xdc\x84\x58\xbf\x7b\xd7\xdf\xde\xbc\xb5\x44\xfb\x2d\x4c\x4d\xfc\x01\x1e\x69\x72\x0b\x7e\xd5\x98\x1d\x2c\xc2\x8d\x95\x12\x00\x4e\x9a\xdb\x7a\x61\x04\x88\x3c\x0f\x1d\x20\x31\xd0\x36\x37\x25\xe8\x92\x10\xd9\x8b\x4f\x3e\xd7\x8a\x9e\x61\x62\xce\x20\x34\xe3\xe4\x59\xdd\x89\xa7\x6c\x01\x73\x3f\xbd\xae\x1d\x21\x62\x9a\x43\x4b\xd7\xcb\x55\x3a\x2e\xff\x1e\xfa\x4f\xa9\x24\xf8\x8c\x94\xa8\xbb\x28\x26\x64\x6d\x9d\x36\x7f\x46\xe0\x0e\xfa\x3e\xb8\x88\xf5\xae\x62\x16\xd6\x2b\xa8\x05\x79\x67\x3d\x41\xd2\x29\x24\x48\x6e\x52\x41\x90\x74\x4a\x07\x92\x9b\xd7\x0c\x54\x0c\xe3\x31\x8e\x71\x97\x65\x7c\x23\x9e\x71\x97\x69\xbc\x0b\xd7\x46\x3d\x48\xe3\x6a\xa6\x46\xca\x45\xb5\x94\xd6\x6c\xd6\xf4\x9c\xc1\x64\x5e\xed\xce\x06\x51\x08\x88\x43\xb8\x6f\xf6\xdd\x31\xc8\xc5\x06\x33\x5f\x5e\x21\x05\x33\xbe\x1a\xf1\x52\x0c\xb0\x6b\x8b\x0f\xc8\x44\x19\xfc\x40\x7e\x94\x49\x2f\x7c\xb6\xbb\xc0\xe9\x8c\x57\x6c\xf8\x64\x87\xb7\x06\x0d\xd9\xb3\x52\xbc\x82\xcc\x2f\xcf\x17\xd0\x39\x83\x5b\xd5\x12\xac\xd3\xec\x29\x6a\x33\x69\x23\xf0\x8e\xef\x24\x3a\x8d\x8e\x96\xda\x37\x14\x0b\x21\xf1\x57\xa7\x9e\x8d\xf6\x5c\xb0\x4f\x35\xd8\xf9\xf2\xca\x9a\x97\x5a\xa5\x75\x6a\xcc\x73\x4c\x3d\x39\x15\x5a\x38\x7d\xb3\xb1\xf1\x7e\xba\x91\xb6\x76\x0c\x3d\xb0\x03\x81\xb1\x1d\x03\xeb\xdb\xed\xbe\xb9\x99\x19\x38\xc2\x6a\xdb\xa3\x00\xba\x34\x11\x7a\x09\xe0\xf5\xd0\xb5\x58\x7e\xba\xc1\x2d\x38\xde\x06\x5c\xda\xd7\xe9\x06\xbb\xf4\xa8\x60\x9f\x36\xb0\xa0\x47\xa7\x79\xaf\x2f\x57\xe0\x51\xf2\x3a\x11\x61\xea\xe3\x56\x7e\xba\x09\x54\x2c\x40\x93\x89\xe2\xad\x39\x1a\xac\xe8\xab\xf3\xc1\xb6\xd7\x1b\xc0\xf6\xb4\xc1\x26\xa3\x86\xc4\xf6\xb4\x87\xed\xd9\x38\xb6\xdf\xd4\xa9\x3a\xa1\xd0\x61\x9f\xa8\x1f\x12\x2d\x66\x8a\x76\x7a\xdb\x7b\x39\x5b\xa2\x17\xa5\xc3\xb2\x05\xc9\xfa\xce\x47\xfc\x40\xfb\x2a\x53\xb9\xe6\xfb\x27\x9b\x7c\x47\x72\x0d\x5a\x97\x19\x0b\x20\x69\x41\x63\x01\xa9\x86\x7e\xda\x42\xdb\x43\x12\x0c\x16\xb3\xe5\x73\x99\xa5\x1c\x77\xe6\xc3\x74\xbe\xac\x9d\x7d\xb9\x84\x44\xcf\x11\xe2\xc5\x0b\x74\x4b\x62\x74\xe2\x41\xf3\x95\x49\xdd\xe9\x87\x0f\x5b\x26\xc1\xb4\xeb\xfe\xc1\x55\x9a\x3e\x41\x07\xda\x73\x9b\xa1\xa3\xae\xeb\x34\x38\x8c\xc8\x9f\xed\x88\xbc\x3b\xe7\xd1\x76\x77\xab\x19\x8f\x7e\x97\x15\x57\x1a\x1a\x98\xed\x18\x32\x17\x05\x37\xee\xf9\xb3\x11\x1a\x4f\x77\xa4\xe1\x1a\xdb\x56\x6c\xb1\xbe\x58\xae\x9d\x56\x02\xe1\xf7\x45\xf9\x54\x3a\xc6\xe9\x1b\x6d\x42\xb1\xb5\x43\xeb\x98\x27\x1b\x6e\x33\xf0\x29\xc8\xb1\xd1\xcf\x1a\x3f\x2e\x4a\xc4\x2a\x18\x02\x21\x5e\x9a\x73\xc2\x53\x0f\xfa\x60\x2c\xda\xda\xbc\x1c\x79\x4d\x00\x30\xc2\x9d\x7a\x75\x77\x24\xb4\xcd\xe5\x4f\xbd\xba\x33\x0a\xce\x32\x6e\x1d\x1d\xa1\x93\x99\x2b\xf8\x6d\x3f\xac\xdf\x70\xc8\x18\x0f\x8d\x48\x0b\x5f\x75\x1c\x6e\xc6\x95\x11\xe3\xde\x2d\xa4\xd6\xad\x4e\x1b\x83\xdb\xbe\xc9\x06\x37\x8d\x26\x5a\x12\xb2\xb7\xcd\x00\x28\x11\x90\x1e\x02\x32\x40\xe0\x94\xa2\xc8\x3d\x56\xcb\x2b\x87\x10\xe7\x9a\x37\x9c\xb6\xae\xf1\x0e\x4d\xfe\xa1\xd8\x97\x3f\xdc\xad\x99\x81\xaf\xae\xf8\x31\xd7\xbc\xe6\xb4\x75\x21\x1d\x23\xfc\xd0\x62\x9c\x2f\xaf\x3e\x7d\x82\xf6\x87\xa5\xe9\x8d\x64\xa0\x6f\xab\xa7\x75\xa6\x21\xc5\xf8\xd6\x9b\xcc\x84\xe7\xa3\x2f\x6d\x1d\x2c\x36\x47\xec\xe4\x2b\xdd\x16\xc2\x25\x1d\x8b\x1d\xff\x5c\xdb\xa2\x0c\x93\x34\xb7\xbe\x2b\x6a\x00\xdf\xcc\xf8\x88\x76\xc3\x69\xa0\xaf\x61\xf2\x6a\x38\x0f\x74\xd3\xbd\x54\xf8\x26\x5b\xa9\x60\x93\x54\xc6\xcb\x79\x77\xbf\x13\xde\x43\x47\x5d\xfe\xf7\xd0\xd7\xfd\x1f\x80\x38\x2c\xd0\x34\xbb\xb9\xfe\x49\x36\x41\x7d\xf2\x1c\x9e\x3e\xcd\x58\x33\x6f\x9c\x83\x44\x47\x46\xd5\xeb\x20\xf5\x2c\xe0\x10\xe7\x91\x71\x33\xdd\xab\xff\xba\xe4\xfc\x6f\x7c\x88\x74\xc6\xd6\xb3\xda\xb8\xb7\xba\x8b\x7e\xc0\xc5\xa7\x4c\x16\x8e\xcf\x09\x6d\x9f\xd2\xdb\xd2\xf9\xdd\xe7\x10\x5b\x7a\xf6\x59\x39\x2d\x35\x54\x13\x73\x7a\xc2\xb9\xd3\xdc\x9c\x86\x4a\x4d\xcf\xe9\xa8\x6e\x3a\xaf\xd8\x8a\xc2\xdd\x89\xa7\x83\x4e\x3c\xbd\x69\x27\x9e\x0e\x3a\xf1\x74\xb7\x4e\x98\x55\x25\x4d\x57\x39\x59\xb5\x44\x2b\x5e\xad\x4a\xfe\x81\x1b\x36\x20\x22\x75\xb8\x5b\xc6\x83\x8b\xcb\xf5\xac\x66\xc3\x24\x22\x03\xe4\xb3\x21\xe4\xa7\x97\x27\x36\x9c\x1e\x6a\x48\x4f\x87\x2e\x6c\x3d\x4f\x74\x4b\xbb\x26\xed\xf1\x4b\x6d\xa1\x34\x84\xb3\xe6\xb0\xd3\x16\x11\x62\xcb\xc5\x9c\xfa\x63\xbb\x3f\xd3\x29\xf6\x2f\xdb\x35\x6f\xb8\x5d\xd3\xdf\x75\xb3\xa6\x3f\xb6\x55\xd3\x77\x6c\xd4\xf4\xbf\x6c\xd3\xbc\xed\x6d\x9a\xfe\x96\x9b\x34\x0d\x6a\xe9\x6c\xd1\xf4\xb7\xd9\xa0\xe9\xdb\x8f\xe1\x37\x1b\x0f\xef\xd3\xe0\xe3\xdb\x29\xc5\xff\x22\xdb\x35\xfb\x05\x76\x42\x4c\x7e\xb3\x3d\x9c\x75\xb9\x1d\x41\xf3\xf7\x55\x6e\xe7\x46\xbb\x2d\xd5\xe3\x76\xb7\x67\x0d\xb3\x53\x41\x9e\x10\x93\xce\xb6\x90\x10\x13\xeb\x36\x13\xba\x65\x41\x1e\x01\xd8\xd9\x6a\x42\x55\x55\x8b\x10\x93\x5b\x3b\x42\xac\x77\xdf\x5a\x93\x67\xb0\xc9\xc1\xdb\x64\x69\x9a\x26\x79\x98\x4f\xb5\x82\x3d\x7b\x53\x13\x64\x44\x12\x46\x12\xc2\xf4\x72\x3e\x7b\x86\xba\x3d\x86\xa6\x09\x0e\x13\x0f\x87\x4c\xaf\xfe\x63\x26\x82\x43\x52\xf0\x4c\xd6\x0c\xaa\x6b\x03\x6d\x49\x24\x8a\x7d\x9f\x44\x91\x2c\x2b\xa4\x2a\x07\x99\x89\x50\x9e\x06\x01\xa3\xb1\x5e\x57\x68\x4b\x22\x79\xea\x65\x84\x7b\xb9\x5e\x86\xc8\x4c\x24\x88\xd3\x30\xa0\x38\xd7\x8b\x14\xf5\x52\xd3\xdb\xae\x52\x24\xec\xe9\x86\x55\x8a\x70\xf4\xa5\x4c\xd1\x2d\xe5\x44\x74\xe7\x32\x45\xa2\xc9\x58\x5e\xa4\xc7\x8c\x61\x66\x44\xbf\x94\x29\xba\xfd\xdc\x88\x6e\x5b\xa6\xc8\xa8\x9c\x6e\x7e\x44\x47\xcb\x14\xf9\xd4\x5d\xa6\x48\x0c\xe3\xf7\x29\x31\x65\x4b\xe4\x5f\x24\x5b\xfa\x97\x3e\xdc\x72\xbb\x07\x5b\x3e\xd3\x91\x95\x9b\x27\x51\xf2\x51\xd3\x5d\x85\xe8\x2f\xf5\x0e\x5e\xc3\x5d\x37\xdd\x4d\xbe\x87\xec\xe2\x62\x7e\x3d\x51\x3f\x4e\x11\x5b\x9d\x5d\x9e\xf3\x45\xb5\xee\xdf\xc9\xa3\x1f\x9f\x69\xf9\x81\x52\x4a\x2d\x89\x1e\x79\x6f\x13\x10\xca\x48\x91\x40\x5e\x91\xc7\x84\x32\x4e\xc8\xde\x74\x08\x17\x63\x3f\x0e\x82\x04\xca\x0c\x12\x9f\x17\x51\x98\xe5\x7a\x6a\x30\x68\x90\x86\x99\x57\xa4\x59\x01\x17\x20\x64\x41\xee\xa7\xa4\x30\x21\xe6\x49\x1a\xe6\x29\x0b\xe1\xf6\x6c\x4c\x93\x3c\x4d\x33\x27\x62\x3f\x09\xa3\x8c\x84\x29\xa4\x33\x7e\x40\xd3\xd0\xa7\x26\xc4\x61\x52\x60\x8c\x0b\xe0\x38\x8d\xbc\x30\xf7\x70\xe2\x44\x9c\x10\xbf\xa0\x84\xc1\x95\xdb\xac\xc0\x49\x50\x24\xa9\x09\x31\x4b\x71\x16\xf2\x1c\x38\xce\x59\x94\x53\x8c\xa9\x13\x71\x4e\xbd\x98\x31\x29\x63\xe6\x7b\xbe\x47\x02\xa3\x8c\x31\xa1\x7e\x98\xca\x3b\x23\x82\x30\xf6\xa2\x22\xe5\x4e\xc4\x24\xf0\x31\x0d\x53\xb8\x3b\x22\xe0\x3c\x48\x09\xcd\x8c\xa2\x08\xbd\x2c\xce\x33\xb8\x40\x3c\x0f\x8b\x22\x0d\x38\x71\x22\x8e\x49\xca\xc3\x3c\x06\x51\x14\x24\x4e\x69\x12\x19\x95\x47\xbd\x9c\xa7\x58\x5e\x5e\xe1\xa7\x38\x4a\xa2\x14\xbb\x65\x9c\xe6\x99\x17\xc9\x0a\x95\x24\xcc\x62\x4c\xfc\xd0\x84\x38\xc3\x49\x5a\x60\xc9\x40\x56\x44\x09\x89\x92\xc0\x89\x98\x07\x49\x1a\x25\x19\xc8\x2e\xe1\x05\x0e\x58\x6e\x94\x31\x2f\x52\x1e\xc4\x14\xae\x11\xf7\x69\x50\x90\x90\xfb\x4e\xc4\x5e\x91\xe1\x24\xcf\xa0\x01\x4d\x69\x96\x87\xa9\x91\x63\x12\x78\x19\xc3\x59\x06\x97\xb4\xc7\x2c\x4b\xb2\x28\x74\x2b\x2f\xe7\x09\xc9\x22\x70\x90\x30\x21\xa9\x47\x62\x23\xe2\x80\xc5\x01\x0d\x18\xbc\x23\x44\x9c\x45\x3c\xa0\x6e\x8e\xc3\x2c\xf5\x58\x92\x03\x27\x69\x1e\xe0\x22\xcd\x03\xa3\x4b\x47\x45\x42\x69\x0e\x88\xa9\x8f\x71\xe8\xa7\x6e\x8e\x13\xea\xf3\x10\x87\x04\x5c\x9a\x47\x51\x5e\x30\xb3\x83\x50\x1f\x67\x51\x04\x19\x3e\xc9\xd3\xc0\x27\xd8\x73\xc7\x0a\xcf\xf3\x49\x9c\x51\x79\xe7\x7b\x91\x12\xec\x1b\xcd\x2d\x2d\xc2\x24\x2e\x32\x55\xdf\x94\x17\x1e\xe7\x6e\xab\xc8\x22\xee\x79\x69\x01\x86\xef\xe7\x8c\xd2\x22\x33\x5a\x45\x1e\xb2\x38\xc1\x01\x20\x4e\x7c\x8f\xb1\x98\xb8\x45\xe1\x45\x19\x8b\xfc\x50\x5e\xef\xe2\x79\x3e\x25\x66\x07\xc1\x01\x49\x48\x22\xdf\xbd\x3c\xe6\xf1\x88\xc7\x6e\x51\x90\x38\x8d\x3d\x46\x21\xb8\x04\x51\x4e\x48\x51\x18\x5d\x9a\x70\x2c\xc4\x04\x22\x0b\x33\x12\x65\x09\x89\x9c\x88\x83\x9c\x64\x51\x5e\x80\x55\x84\x2c\x0b\x08\xe3\xb9\x31\x56\xf8\x3e\xf5\x72\x0c\x22\x4b\xf2\x24\x4c\xfd\xbc\x70\x22\x8e\x42\x8f\xc5\x7e\x18\x48\x07\x61\x45\xe4\xe7\xdc\x6c\x6e\x11\xf3\x58\x0a\x71\xdb\xcf\xe2\x38\x25\xcc\x1d\x36\x29\xce\x48\x96\x10\x19\xdd\x62\x9e\x33\xce\x23\x13\xe2\x84\xc4\x84\x64\x52\x64\x38\xa0\xc4\x0f\xfd\xd4\x89\x98\x91\xb4\xe0\x94\xc9\x38\x9b\x15\xd8\xf3\x23\xa3\x83\x30\x8a\x59\x14\x05\xc0\x71\x9a\x05\xc4\xf7\x3c\x77\x74\xcb\x48\x90\xd2\x34\xf6\x20\xce\x7a\x05\x4d\xe2\x04\x1b\xa3\x5b\x1c\x65\x21\x66\x20\x63\x2f\x0a\x83\x94\xfb\x6e\xab\xc8\x71\x42\x38\xc5\x09\x20\x8e\x78\x11\x12\x6c\x1c\xf3\xf2\x28\x49\xbc\x88\x80\x2e\xc2\x30\x0a\x59\x32\xe2\x79\x45\xe0\x71\x3f\x94\xb2\x0b\xe3\x18\x13\x8f\x30\xa3\x1d\x7b\x11\x63\x9e\xec\x99\x4f\xd2\x34\xc7\xa9\x5b\x79\x38\x61\x41\x86\x31\x84\xcd\x94\xe6\x24\xf7\x32\x23\xc7\x98\xfb\x71\x94\x79\xd2\x8e\x71\x80\x59\x1a\xba\xa3\x1b\x89\x03\x1a\xc7\x01\xd8\x71\x5e\x50\xce\xd3\x24\x31\x21\xf6\x83\xd4\x4b\xb3\x14\x7a\xc6\x71\x92\x06\x74\xc4\xdc\xfc\x04\x67\x5e\x96\x82\x52\xb2\x30\x4b\x42\x16\xf9\xc6\x78\xcc\x73\xca\x58\x00\x61\x93\xfb\x01\xa6\x2c\x73\x9b\x5b\x98\x26\x59\xc6\x82\x42\x8e\x0c\x91\xcf\xfd\xd8\x88\x38\xa2\x84\x47\x85\x0c\x56\x79\x94\x92\x94\x32\xb7\x28\xe2\x80\x16\x94\x70\x70\x90\x30\xe7\x45\x4a\xcc\xb1\x22\xa6\x2c\x8c\x7c\x39\xd2\x04\x3e\x8e\x49\x11\xb9\xad\x82\x06\x19\x8d\x29\x96\x99\x10\x2e\x3c\x96\xc6\xc6\xb0\x49\xb3\x2c\xf6\x88\x54\x1e\x66\x51\xe0\x27\xdc\x9d\xbb\x25\x5e\xca\x8b\xa2\x60\x32\x8b\x8c\x7c\xcc\x89\xd1\x2a\x58\x10\x7a\x51\xc6\xc1\xf3\x72\x4e\x49\x9a\x73\x77\xee\x96\xf2\x22\x61\x7e\x21\x47\x06\x92\x45\x71\x82\xcd\x79\x45\x14\xe3\x98\x16\x72\x08\xf3\x63\x12\xfa\xc4\xad\xbc\x8c\x91\xd8\xe7\x19\xc8\x98\x33\x12\x45\x38\x31\xca\x38\xc7\x34\x4a\xa9\x1c\x9a\x88\x30\x24\xd2\x9d\x04\x1c\x26\x22\x2c\x67\x71\x9e\x83\x83\x64\x39\xf7\x78\x8a\x8d\x61\xb3\x08\xe3\x3c\x28\xe2\x42\x0d\xba\x3c\xc7\xb1\xdb\x8e\xbd\xa8\xf0\xa2\x58\xe6\x0b\x31\xc1\x71\x54\xa4\x46\x97\xf6\x58\xe4\xc7\x79\x06\x0e\xc2\x48\x46\x13\xca\xdc\x23\x08\xc6\x7e\x91\x50\x2f\x50\x13\x77\x89\x97\x33\x23\xc7\x38\x8d\xb1\x97\xfa\x32\x1e\xfb\x38\x0b\x62\xec\x96\x31\xa1\x79\x1a\xc7\x45\x28\xad\xc2\x0b\xe2\x9c\x1a\xe3\xb1\x4f\x32\xc6\xd2\x18\xac\x22\xf0\xb2\x98\x04\x89\xdb\x41\xfc\x2c\xe1\x29\xf7\x40\x14\x38\xcc\x92\x94\xa7\x46\xe5\x05\x3e\xce\xa3\x38\x83\x9e\x25\x19\xf6\xbc\x3c\x70\xdb\x71\x90\x65\x61\x1e\xc8\xc4\x3b\x4b\x7d\x1e\x90\xd4\x38\x34\x89\x74\x85\x24\x09\x04\xab\x22\x8b\xc2\x98\x8b\xf0\xea\x8a\x15\x45\x96\x46\x05\x93\x83\x24\xcb\xa3\x82\x71\x23\xc7\x51\x16\x04\x38\xa1\x80\x38\x60\x41\x1c\x52\x1c\xab\x49\xd4\xb7\x8e\x63\xab\xed\x7b\xe1\xcf\x37\x3d\xa1\x6a\xbb\x06\xed\xe7\xce\x09\xd5\xbf\xdc\xec\x84\x6a\x88\xc9\x76\x4b\x07\x86\xe5\x88\xdb\xaf\x3e\x7a\xd3\xa5\x83\x88\x79\x09\xaf\x27\xdc\xfd\x34\xcb\x12\xcf\xb2\x74\x90\xa6\x51\xcc\xb8\x1c\x7e\x69\x90\x31\x16\x77\x53\x17\x07\x11\x3f\x8b\x78\xe1\xc7\x10\xc9\x0a\x9e\x04\x05\x15\x91\xcc\x04\xc9\xc2\xa0\x28\x42\x1f\xbc\x20\x2c\x70\xee\x47\xc5\xb6\xb3\xfa\x21\xf6\x78\x48\x64\xf0\x61\x39\x8f\x28\xc9\x2d\x4b\x07\x49\xea\x85\x11\x95\x06\x49\x52\x9f\x47\x19\x2e\xb6\x24\x82\x0b\xea\xe7\x89\xb4\xf9\x22\x0d\x70\x9a\x47\x96\x9e\x84\x29\xf7\xb2\x5c\xa6\x41\xd8\x8f\x39\xc1\x71\xb2\xcb\xd2\xc1\x6d\x9f\x23\xdd\xa6\x34\x2c\xc0\x79\xf6\xca\xaf\x4f\xb0\xbd\xf4\xeb\x13\x62\xaf\xfd\xfa\xc4\xb7\x17\x7f\x7d\x12\xd8\xab\xbf\x3e\x09\xed\xe5\x5f\x9f\x44\xf6\xfa\xaf\x4f\x62\x4b\x01\x58\xd9\x41\x28\x0f\x6b\xdc\x07\x2e\x9f\xcf\xe5\xf3\xe1\x61\x0f\x29\x03\x68\x6e\x3c\x02\x25\x9f\xcf\xe5\x73\x4b\x73\x02\xcd\x89\xb5\x39\x99\xcb\xe7\x96\xe6\x3e\x34\xf7\xad\xcd\xfd\xb9\x7c\x6e\x69\x1e\x40\xf3\xc0\xda\x3c\x98\xcb\xe7\x96\xe6\x21\x34\x0f\xad\xcd\xc3\xb9\x7c\x6e\x69\x1e\x41\xf3\xc8\xda\x3c\x9a\xcb\xe7\x96\xe6\x31\x34\x8f\xad\xcd\xe3\xb9\x7c\x6e\xd8\xd6\xb7\x65\xd1\x63\x69\x19\x26\xe4\x4c\x1a\x45\xbf\xe2\x1e\x6c\xb9\x95\x06\x61\x6a\x95\x4a\x5b\x30\xb5\xca\xa4\x1d\x98\x5a\x65\xd2\x04\x4c\xad\x72\xa9\x7e\x53\xab\x5c\x6a\xde\xd4\x8a\x4b\xad\x9b\x5a\x71\xa9\x70\x53\xab\x42\x2a\xdb\xd4\xaa\x90\x7a\x36\xb5\x3a\x93\x3a\x36\xb5\x3a\x93\xea\x35\xb5\x9a\x49\xd5\x9a\x5a\xcd\xa4\x56\xe7\xa6\xba\x83\xae\xa3\xbb\x5b\x5e\x87\x6a\xad\xa7\x5d\xd3\xff\xb9\x94\xb5\x87\x6d\xc7\xcd\x1f\xc1\x08\x5e\x2f\x9f\x0d\x41\xb6\x28\x14\x2d\xc9\x08\x11\xfc\x5c\xd6\xa7\x0d\xf4\xaa\xd1\xe8\x6b\x44\xde\x02\xa4\xb9\x96\x6b\x8b\x63\x2e\x71\xa8\xf3\x05\x7d\x1c\x70\x6a\xfe\x46\x15\xa8\x8f\x8e\xd0\x7f\x40\x35\x62\x3b\xf1\xba\xa4\xf3\x4e\x15\xaa\x37\xb3\xa6\xce\xf1\x66\xec\x2c\x9e\x02\x9b\x6b\x2d\xdc\xe7\xf1\x24\xd4\xac\x53\x05\x7b\x26\x8b\xff\xea\xc5\xab\xe7\x50\xa2\xb8\x2e\x07\xdc\x81\xa3\x03\x38\xd8\xf4\xfa\x0e\x75\xc1\x62\xd7\x09\x53\x09\x39\xef\x70\x31\x1f\x72\x31\x33\x71\x31\x1f\x72\x31\xd3\xb9\xe8\xc2\xc5\x43\x38\x4b\x25\x63\x5d\xa5\x96\x9a\x39\x1f\xb4\xda\xdb\xbb\x14\xdf\x6e\x35\x8a\xb7\xd3\x28\x6e\x35\x8a\xb7\xd2\x28\x9e\x75\x0a\x7c\xcf\xea\x2a\xdc\x5a\x61\xee\xb9\xaa\xd5\xad\x09\x09\x2b\x09\x77\xc1\x60\x1f\x73\xa2\xa9\xb4\xc6\x17\x8d\xaa\x14\xcf\x3b\x6c\xcc\x0d\x6c\xcc\x4c\x6c\xcc\x07\x6c\xcc\x3a\x6c\x74\x11\x46\x03\x7c\x24\x72\xea\x74\xa7\xda\xe1\xae\x50\x12\xb7\x6a\x8f\x5d\x6a\xff\xb9\x8c\x65\xe4\x32\x0e\xcc\x3d\xc8\xb9\x82\x74\x9c\x09\x97\x90\x38\xd2\x02\x89\xf5\x56\xe8\x1a\x56\x32\x80\x8d\x99\x45\x1f\x76\x5e\xc3\x8e\xf2\xd0\x46\x9a\xb9\x10\x5a\x19\xf7\x47\xae\x2e\x78\x1b\xca\x66\x12\x7c\x06\x35\xdb\x04\x1e\xa1\x49\x6f\x0f\x3d\xac\xbd\xb3\xf9\xe5\x7f\x20\x8c\xee\xa3\xc1\xb6\xe9\x21\x1f\xe2\xdf\x5a\x83\xe3\x6c\x88\x7f\xf7\x1b\x6f\xb1\x70\x81\x6f\xca\x05\x48\x71\x4b\x1e\xa4\x76\x86\x1c\x48\x4d\x0c\xe8\x9b\x91\xb6\xa3\xe2\xcf\xa5\x4d\xbd\xed\xa8\xf7\x73\x69\x62\xce\x5e\x13\x5f\x15\xc5\x9f\xa1\xbb\xa8\x98\xa9\xb2\xf8\xe2\x8b\xf9\x1c\x9f\x6c\x23\x7d\x9f\xcf\x45\x9b\xb9\x6a\x23\xbe\x9c\xcd\x1d\xc5\xf4\x67\x50\x4d\x5f\xa0\x4e\x25\x1d\xf8\x9c\xc9\xcf\xa9\xfa\x6c\x6f\x3e\x87\xe6\x82\x4a\x2a\x49\xc2\xe7\x4c\x7e\x4e\xd5\x67\x77\x49\xfe\x99\xac\xc9\xaf\x02\x8e\x1c\x57\xd8\x5c\x96\x97\xde\x93\xc5\x0f\xd8\xac\xae\xd8\xaf\x1e\x76\x6a\xf6\xcf\xb4\x5b\x24\x58\x3d\xea\x38\x2b\xf3\xc3\xdb\xd4\xa4\x41\xa4\x68\xce\xba\x34\xe7\x1d\x9a\xb3\x2e\xcd\xb9\x4e\x73\xb6\x0d\x4d\x2c\xfb\xc9\xd5\xd0\x20\xcf\x9b\x70\x39\x28\xd0\xba\xec\xff\xac\xbe\xb4\x42\x7b\x18\xb4\x0f\x05\x4d\xbf\x7e\x26\xcb\x70\xbb\x69\xca\x7e\x2a\xe0\x9a\xe6\xac\x4b\x73\xde\xa1\x39\xeb\xd2\x9c\xeb\x34\x67\x2d\x4d\x63\xd6\x39\x7e\x0f\x81\x99\xd7\x1f\xa1\xfa\xd2\x8f\xf6\xc3\x54\x3f\x82\xf3\xfe\x58\xba\x8e\x51\xfd\x08\xc1\xe0\xc7\xd2\x16\x42\x3f\xc0\x45\x09\x02\x66\x36\x6f\x58\x34\x39\xa5\x04\x14\x04\x67\x6d\x5f\x64\xb8\xa8\xb0\x1e\x2e\x66\xdb\xc4\xaa\x96\xac\xf8\x57\x48\xc4\x4d\xb3\x02\x52\xd9\xcc\x44\x30\xbb\x11\xc5\x1f\x8d\xa1\xa7\x4f\xf1\xc7\xd2\x44\xf1\xc7\xf2\x26\x14\xcd\xc1\xae\x4f\xf1\x67\x23\xc5\x9f\x4d\x14\xcd\xd6\xd6\xbf\xbc\xc2\x42\x12\x26\x2f\x6a\xb7\x07\x40\x2b\x77\x30\x0f\x52\x47\xa5\x7d\x19\x1e\x81\x45\xa2\xb3\x58\xe3\xda\x8e\xcd\x3f\x5f\xe4\xac\xe2\xe8\xca\xfd\xa6\x2f\xfe\xe0\x7d\xd3\x68\xdf\xf0\xba\x79\x66\x62\x1b\x06\xa0\xc2\xd4\x06\x5e\x6c\x0b\x53\x1b\x78\x87\xe6\xa6\x36\xf0\x0a\xcd\x4d\x6d\xe0\x95\x7c\x92\xcf\xe1\xfa\x8e\xb9\xed\xfe\x0e\x78\xa7\x9f\xe4\x33\x80\x92\xa2\xe3\xba\xe4\xf2\x81\xd0\xac\x37\x81\x08\x4c\x99\x89\x47\x98\x52\xc8\x4c\x3c\xc2\xec\x45\x6a\x6a\x03\x93\x17\xa9\xa9\x0d\xcc\x93\x30\x53\x1b\x98\x26\x19\xdc\x66\x20\xfe\x60\xda\x65\x22\x4d\xbd\x22\x56\x61\xc0\xc4\xcd\x44\xca\x41\x58\xd6\x7e\x3b\xe2\x48\x69\x54\xc3\x64\xe7\x56\x2f\x2b\xd1\xe6\x0c\x21\x33\x78\x02\xf6\xcf\x06\xd9\xc0\x93\xa6\x18\xc5\xe4\x09\xd8\x3d\x93\xcc\x3e\xf1\x74\x6e\xd9\x90\xd9\x3e\x1e\x6d\x96\x51\x12\x04\x11\xa5\x43\x82\xb8\x25\x08\xe2\x49\x15\xc1\x4e\x24\x48\xc7\x09\x6a\xf3\x92\x92\x20\x81\x10\x3b\x24\x48\x5a\x82\x64\x56\x8f\x4b\x13\x80\xd7\xc2\xeb\x38\x41\x6d\x26\x53\x12\xf4\x05\xc1\x7c\x48\xd0\x6f\x09\xfa\x82\x56\xae\x08\xfa\x23\xee\xd0\xc7\xa3\xcd\x7d\x4a\x82\x81\x20\xc8\x87\x04\x83\x96\x60\x20\x68\x71\x45\x30\xd0\x09\xf2\x71\x82\xda\x6c\xa9\x24\x18\x0a\x82\xc5\x90\x60\xd8\x12\x0c\x05\xad\x42\x11\x0c\x75\x82\xc5\x38\x41\x6d\x7e\x55\x12\x8c\xe0\xa5\x62\x48\x30\x6a\x09\x42\xf6\x7e\xa6\x08\x46\x9d\x97\x88\x71\x82\xda\x8c\xac\x24\x18\x0b\x82\xb3\x21\xc1\xb8\x25\x08\xaf\x4d\x6a\x4c\x16\xf0\xae\x24\xe0\x93\xcf\x5e\x7c\xb9\x14\xe7\xf6\x2e\xc5\xc1\x22\xb9\x57\x37\x9b\x09\x64\x50\x87\xc5\xf7\x6e\xfb\x5a\x1c\x33\x19\xfc\x4f\x79\x31\xce\xc9\x72\xf1\x81\xaf\x64\x95\x5f\x54\x2d\x91\x4f\x0e\xd2\xb2\x12\x09\x4a\x8e\x18\xec\xcf\x4e\x79\xb1\x5c\x71\xb5\x9d\x7a\xa0\x35\xed\xac\x89\xb6\x76\x57\x2d\x5f\xfb\xe4\x36\x2e\xe2\xf9\xbd\x5e\xc1\xa3\xf3\xd9\xd4\x07\xb9\x8f\xb0\x47\x82\x23\x5f\xd5\x29\xfe\x72\xba\xc9\x7a\x54\x29\xc4\x64\xd7\xd3\x4d\xa2\xc9\xc8\xe9\xa6\xce\xb6\x86\xc1\xe9\xa6\x10\x93\x2f\xa7\x9b\x6e\xfb\x74\x93\xd0\xca\x76\xa7\x9b\x8c\xca\xe9\x9c\x6e\x92\x0a\x72\x9e\x6e\x92\xe7\x68\xb7\x3c\xfd\xed\xff\xae\xcf\x33\xf1\x45\x76\x90\xb2\x35\x8f\x82\xde\x83\xf3\x3c\xec\x83\x7e\xb8\x78\x9f\x17\xbd\x1f\xb3\xf2\x62\xc6\x57\xbf\xc9\x91\x28\x8d\x55\xf8\x2e\x38\x94\x0f\x24\x63\xf0\x59\xe7\xe7\x5f\xe1\xe8\xd4\xcf\x5b\xdd\x09\x04\x9b\x67\x4e\xa0\xeb\x0d\x9c\xf6\xdb\xf8\x51\xa8\xa3\x23\xf4\x82\xaf\xce\x61\x14\x3d\x99\x2d\xcb\x8c\x23\xdc\xbf\x36\x45\x34\x7f\x71\x82\xbb\x67\x97\xc2\x78\x8a\x82\x64\x8a\x02\x3c\x45\xbe\x3f\x45\x24\x9c\x22\x1c\x4f\x51\x32\x45\x08\x6b\x5b\x8d\x42\x3a\x45\xa1\x37\x45\x01\x99\x22\x3f\x98\x22\x12\x4d\x11\xa6\x53\x84\xbd\x29\x22\x3a\x5c\x32\x45\x21\x9e\xa2\xc0\x9f\x22\x3f\x9c\x22\x12\x4f\x11\x4e\xa6\x08\x0b\xfc\x1a\x5c\xe4\x4d\x51\x48\xa6\x28\x08\xa6\xc8\x8f\xa6\x28\xf2\xa7\x28\x0c\xa7\x28\x88\xa7\xc8\x4f\x34\x40\x1f\x4f\x11\xf1\xa7\x08\x87\x53\x14\x4f\x11\x8a\xc8\x14\x85\xc1\x14\x05\x70\xb5\x80\x0e\x28\x38\x21\x53\x84\x83\x29\x8a\x04\x20\x9e\xa2\xd0\x9f\xa2\x20\x9c\x22\x3f\xd6\x00\x49\x32\x45\x04\x4f\x11\x16\x24\xa7\x08\x11\x3a\x45\xc4\x9b\x22\x2c\xd8\x91\x60\x6f\x1d\x72\x25\x66\xb9\x92\xae\x5c\x05\x17\x42\x8e\xa2\xdf\x44\x7c\x9e\x22\x14\xea\xdc\x2a\xc2\xa2\x5b\x82\x5b\x60\xc8\xd3\xb9\xf4\x95\xe0\x04\x57\x02\x20\x9a\x22\xbd\xbb\x38\x92\xf2\x10\x02\x06\xee\xfd\xae\x22\x84\x42\x85\x80\x85\xfc\xfc\x58\x0a\x36\x0c\x7b\xf2\x0a\x3c\xa5\xad\x50\x6a\x3f\xd0\x29\x08\xd5\x08\xd3\xf0\x85\x4a\x23\xa9\xf6\x50\xd7\xa1\x50\x81\xb0\x07\x61\x17\x42\x87\x42\xb0\x75\x56\xd3\xb9\x11\xea\xf2\xfc\x72\xce\xe0\x9a\x14\x91\x54\xae\x67\x65\x31\xb8\xe1\x09\xbc\xe0\x87\xd3\xbf\xbc\x7a\xf2\xc3\x63\x79\xa7\x94\x90\x18\x99\x22\xe8\xbc\x90\x10\x15\x16\xa9\xd4\x04\xd2\x55\x96\x8a\x95\x3a\x89\xb2\x5e\x10\x08\xd5\xe9\xbf\xfa\xee\xf9\x6b\xbe\x46\x6c\x91\xab\xda\xe8\x17\xa0\x52\x79\x9f\x86\x81\x0f\x01\xff\x97\x17\x5d\x7d\xf6\x52\x4a\x6f\xe3\xdd\x87\x97\x11\x4a\x3c\x6f\xda\x7f\x56\xbf\x2b\x48\x10\x03\x00\xe9\x00\x50\xcf\x23\x03\x10\x5f\x03\x19\x3e\x0d\xf4\xa7\x06\x02\x61\x97\x00\x31\x10\x88\xba\x4c\x9a\x40\xe2\x5e\x3f\x0c\x84\x68\x87\x91\x21\x8a\xa4\x4f\x65\x88\x82\xe9\x20\x26\x80\xb4\x2f\xad\x21\x48\xd6\x23\x33\x00\xc8\xfb\x5d\x19\x82\x70\x0d\x64\x48\xa1\xe8\x72\x39\x6c\x4e\x5d\xad\x31\x1d\xd5\x07\xa1\x23\x04\x7c\x3a\x62\x55\x41\x9f\x88\xc1\x2e\xa8\xdb\x6e\x22\x3a\x6a\x98\x31\x75\x19\x26\xa5\xa3\xfa\x4e\xe8\x88\xbe\x59\x9f\x09\x83\x49\xf4\xc9\x0c\x39\xc9\xe8\xa8\xc6\x73\x3a\x62\x35\x9c\xba\xad\xbb\xe8\xd3\x30\x68\xde\xaa\x2e\x15\x25\xb0\x59\x90\x44\x7b\x6a\x51\xa6\xdf\x01\x31\x52\x0f\xba\x58\x4c\x7d\x0c\x75\x10\xa3\x4d\xe8\x7c\x1a\x9e\xc7\x5d\x36\x1c\xbe\x81\x1d\xe6\x9f\xf4\x39\xb5\x06\x0a\xec\xd0\x68\xda\xed\x8c\xc1\x2a\x3a\x9d\xb1\xc6\x09\xec\xb0\x5f\xde\x03\xb1\x85\x0a\x6c\x0e\x05\x74\x54\x14\x98\x8e\x8a\x82\xd0\x51\xd5\xfb\xd4\xad\xb6\xa0\x87\xc2\x16\x2b\x5c\xe2\x8e\xa8\xcb\x84\x63\x3a\xa2\x0c\x4a\x47\x24\x99\xd0\x51\xd3\x62\xd4\xad\xd0\xb4\x2f\x6f\xc3\xe0\xd1\xa7\x32\x04\xc9\xa9\x4b\xa5\x9c\x8e\xb8\x50\xd1\xd7\xa8\x7e\x47\xd5\x74\x2c\xcb\x08\x3c\x8f\x06\x1e\xb6\x46\x10\x05\x63\x4d\x33\x1a\x05\xda\x22\x48\x4d\xc4\x33\x11\x09\xba\x44\x8c\x30\x61\x17\x8f\x91\x99\xa8\x8b\xc7\x08\x13\xb7\x30\x06\x2a\x7a\xb0\x35\x36\x4f\xfa\x24\x0c\x48\x58\xbf\x3b\xf6\x84\x43\x11\x32\x20\xc9\x3a\x82\x35\x00\xe4\x2d\x80\x35\x80\x48\x16\x0c\x8d\x8b\xbe\x56\xac\x79\x97\x53\x98\x98\x8e\xf4\x82\x50\x97\xb4\xfd\x3e\x09\x93\x6d\xd0\x9e\xde\x4d\xb6\x41\xc7\x05\x1e\xd1\x11\x43\x8d\xe9\xb8\xa1\x52\x3a\xa2\x94\x84\x3a\x94\xc2\xa8\xdb\x97\xd2\x3e\x07\xf6\x40\xe2\x74\x95\x9c\x8e\x18\x31\xef\xcb\xd4\x1e\x4f\xac\x16\xa4\xbf\x80\x18\x9e\xe2\x2d\xdc\x1e\x93\x2d\x9c\x09\xfb\x5b\x38\x3e\x0e\xb6\xb0\x67\x1c\x3a\x5d\x1f\x47\x63\x2e\x89\xe3\x91\x60\xa8\xa7\xe0\x66\x0c\xc9\x58\xb8\xc4\x6c\xcc\xef\x71\xba\x45\xb4\xc4\xd9\x58\x20\xc3\xf9\x16\xc1\x12\xf3\x2d\x42\x19\x2e\xfa\x1a\x32\x9a\xcb\x58\xa8\xc0\x78\xcc\x43\x31\xd9\xc2\x41\xb0\x3f\xe2\x65\x38\xd8\x26\xb0\x85\x5b\x84\x1d\x1c\x39\xa3\x1b\x8e\xb7\x08\x4b\x98\x6e\xe1\x8b\x38\xd9\xc2\xeb\x31\xdb\x22\x9a\xe2\x74\x2c\x82\xe1\xcc\x15\xc2\x70\x3e\x16\x16\xf8\x16\x61\x14\x17\xbd\x08\xb5\x4b\xaa\x82\xbd\xc0\x12\x8c\xcc\x2c\x93\x8e\x54\xb0\x35\x45\x91\xb8\x4d\xd8\x03\xed\xb9\x67\x78\x1e\xf6\x94\x33\x84\x88\x3a\x42\x33\xd1\x88\x3b\x10\xe3\xc3\xb1\x3d\x37\x69\xa9\xd8\x32\x93\xba\xa7\xb6\xac\xa4\xe5\x62\xc8\x67\xd6\x93\xe6\x10\x22\xef\x48\xcb\x96\x9a\x00\x06\x4b\x5a\xa2\xda\x9a\x25\xe0\xea\x1e\xa6\x63\xec\x13\x6a\x37\x14\x9f\x8e\x19\x4a\x40\xc7\x14\x1d\x52\x77\xe7\x23\xea\x36\xa5\x58\x7b\x3e\x7c\x4a\xa9\x5d\x74\x09\x75\x89\x8e\xd1\x31\xf3\x4a\xa9\xdb\x09\x32\xea\x36\x9d\x9c\x8e\x19\x06\xa7\x63\x4e\x50\xd0\x31\x13\xef\xa4\x15\x16\x23\xc0\x23\xee\x8a\xc9\x88\x85\x62\x7f\x34\x64\xe0\xc0\x69\xa9\x38\x1c\x75\x78\x1c\x8d\x46\x0d\x1c\xbb\x22\x31\x1d\xf5\x44\x9c\x8c\x86\x0c\xcc\x1c\xde\x88\xd3\x91\x70\x81\xb3\xd1\xa8\x85\xf5\x70\x60\x20\xc1\x47\x62\x2f\x2e\x46\x43\x92\x4a\x2d\x9c\xdd\xc4\x4e\xbf\xc2\x64\x3c\xb4\xf8\x8e\xc8\x81\x83\x11\xb7\xc6\xe1\x68\x6c\xc1\x91\xd3\x81\x71\x3c\x1a\xdb\x30\x1d\x09\x3e\x38\x19\xf5\x40\xcc\x46\xc2\x00\x4e\x47\x63\x20\xce\x46\x43\x01\xce\x47\xe3\x11\xe6\x8e\x60\x87\x8b\x6e\x34\xda\x25\x7f\xa0\x9e\x24\x69\x8e\x2d\x75\xf6\x89\xbd\xc0\x92\x4a\xd4\x4c\x1b\x9e\xfb\x2d\x86\xc0\x6c\x88\x81\xdd\x88\xc2\xae\x44\xcc\x39\x44\x93\x1c\x9b\xc8\xc7\x5e\x27\xfd\xb3\x8f\x9f\xf5\x8a\x8a\x39\x83\x68\x75\x6b\xce\x1f\xe4\x73\x73\xee\xd0\x8a\xcf\xb6\x82\xd2\x8a\xc7\x80\x23\xd7\xbc\xd4\x92\x39\xd4\xe6\x6d\xce\x1d\x5a\x05\x5b\xfa\xef\xd4\x2f\xa6\xf6\xee\x11\x3a\xc6\xbc\x4f\xc7\x04\x10\x50\xb7\x8a\x43\x3a\xd6\x85\x88\x5a\xed\x27\xa6\x63\xc6\x47\xa9\x4b\x7e\x49\x97\xb8\x2d\x89\x70\x58\x47\x4a\x5d\xda\xcb\xe8\x98\xf5\xe5\xd4\x6d\xbf\x9c\xba\xdd\xaf\xa0\x63\x1e\x82\xbd\x11\x17\xc1\x78\xc4\x0b\x31\x19\x75\x43\xec\xbb\x46\x0a\xa7\x85\xe3\x70\xd4\x45\x70\xe4\x8d\xe9\x09\xc7\xa3\x91\x0c\xd3\x51\x6f\xc1\xc9\x68\xb8\xc0\x6c\x34\xe0\xe1\x74\x24\x66\xe2\x6c\x34\x6e\xe0\x7c\x24\x2c\x61\xee\x88\x4b\xb8\x70\x86\x0d\x99\x3d\xb8\xfb\x80\x47\xfd\x12\x13\xbb\x63\x62\x7f\xc4\xed\x71\x30\x62\xf8\x38\x1c\xf5\x1d\x1c\x8d\x47\xb7\xd8\x11\xde\x30\x1d\x77\x9e\xc4\x19\x3f\x30\x1b\x8d\x7f\x38\x1d\x0d\xa2\x38\x73\x06\x11\x9c\x8f\x46\x29\xcc\x47\xc2\x14\x2e\xba\x71\x64\xb7\xe4\xc1\x18\x53\x6a\x7e\x6d\x2b\x24\x0d\x37\xc6\x94\xe1\xbe\xb6\x5d\xc3\x98\x31\x28\x00\x98\x4f\x31\xe6\x0d\x4d\xce\x67\x78\x1e\xd5\x08\x6c\x00\x71\xcb\xa0\xe1\xa9\xae\x73\x5b\xca\xd0\xf2\x67\xc9\x19\xda\x1e\x1a\x28\xa4\x2d\x83\x66\x16\xb2\x0e\x80\x69\xe0\xb0\xfa\x1e\xd7\x95\x63\x40\x5d\x74\x84\x63\x9e\x73\x70\xb5\xc7\x74\x44\xb8\x84\x7a\x36\xc3\xf1\xa9\xdb\x70\x02\xea\x32\x9c\x90\x8e\xd8\x45\x44\x47\xa4\x16\xd3\x11\xd3\xa3\x74\x44\xb5\x09\xb5\xc9\x9d\xd1\x11\x9d\xa6\xd4\x6d\xb5\x19\x1d\xb1\x9a\x9c\x8e\x68\x8e\x53\xb7\xe1\x16\xd4\x65\xf6\xd8\x73\xba\x2d\xc6\x9e\x55\xaf\x98\x8c\xf9\x34\xf6\xc7\x7c\x12\x07\x23\x5e\x8d\xc3\x31\xa7\xc0\xd1\x58\xe4\xc0\xf1\x88\x6f\x37\xe3\x9e\x55\x8d\x38\x19\x73\x20\xcc\x46\xe2\x23\x4e\xc7\x22\x08\xce\x9c\x11\x0a\xe7\x63\x11\x06\x73\xfb\xe0\x5c\x8c\x44\x08\xc8\x0f\xdc\xba\xc2\x23\x96\x86\xc9\x88\xa7\x63\x7f\xcc\x99\x71\x30\xe6\xac\x38\x1c\x0b\x55\x91\x3d\x14\xe1\x78\x2c\x58\x60\xea\x76\x97\x64\xcc\xe1\x31\xb3\x06\x0b\x9c\x8e\xf9\x32\xce\x46\xc2\x05\xce\x9d\xc1\x12\xf3\xb1\x50\x86\x8b\x5e\xc0\xd9\x25\x2b\x50\x6c\x53\x53\x14\xa9\x71\x9a\xf2\x02\xd9\x96\x98\xfb\xec\xb7\xcf\x89\x09\x77\xd0\x4a\xc4\x88\x3f\xd4\xfb\x63\xca\x0a\x9a\xa7\x43\xdc\x71\xc7\xa0\xad\xa3\xa2\x31\x1b\xd0\x98\x1a\x22\x66\x35\x59\x23\xcb\xa9\x32\x50\x53\x06\xa0\xc9\x6a\xf8\x3c\xd7\xd0\x0e\x9f\xf2\xa6\xaf\xc3\x67\x45\x47\xca\xa6\x9e\x3a\x95\x84\xa9\x5b\x49\x84\x5a\x7a\xe4\x53\x97\x76\x02\xea\xea\x4f\x48\xdd\x56\x17\x51\xb7\x65\xc4\xd4\x2e\x0f\x4a\x5d\x76\x91\x50\xbb\x3d\x33\xea\x56\x7d\x4a\xdd\x3a\xcc\xa8\xc5\xa6\x72\xea\x56\x11\xa7\x2e\x9b\x2a\xa8\xdb\x94\xb1\x37\xe2\x47\x18\x8f\x18\x1f\x26\x23\x9e\x8a\x7d\x87\x01\xe2\xc0\xe9\xa7\x38\x1c\x71\x45\x1c\x79\x23\x31\x28\x76\xfa\x5c\x93\xc1\x5a\x78\x4f\xac\x51\x9b\xd9\xbc\x15\xa7\x23\xa1\x0d\x67\x8e\xb8\x88\xf3\x91\x18\x82\xf9\x88\xcf\xe2\xc2\x19\xdc\xc4\x88\x6e\x61\x1c\x3b\x4d\x09\x13\xa7\xd3\x62\x7f\xc4\x2f\x71\x30\xe2\x98\x38\x74\x78\x26\x8e\x46\x62\x0d\x8e\x47\x83\xd5\x88\x27\xe1\x64\xc4\x47\x31\x73\x04\x00\x9c\x3a\xa3\x16\xce\x9c\xa1\x05\xe7\x36\xff\xc7\x7c\xcc\x85\x8b\x6e\xe8\xd9\x7d\xe8\x36\xd8\x48\xcd\x6a\xe0\x61\xc3\xd0\xad\x52\x0d\xc3\xa0\xad\x90\x9a\x9a\x05\x4d\x92\x63\x7a\x1a\x5a\xba\x1f\x49\x94\x86\x31\xba\x4d\x99\x86\x4f\xa9\xd6\x01\xd3\x30\xdd\xf4\x7d\xd8\x94\x69\x46\x3e\x7c\x9a\x6a\x9d\x30\xbd\xaa\x6b\x79\x9c\x61\x98\x96\x72\x1b\x62\xe5\xad\xdc\x4c\x2f\xe9\x5a\xe6\x3b\xec\xa9\x4b\x0c\x98\x9a\x85\x4a\xa8\x4b\xbf\x3e\x75\xf5\x31\xa0\x0e\xc3\x09\xa9\x4b\x78\x11\x75\xf5\x24\xa6\x36\xf1\x50\xea\x30\xab\x84\xba\x54\xcd\xa8\x4b\x23\x29\x75\x18\x42\x46\x6d\x66\x9e\x53\x97\x25\x73\x6a\xb6\xd8\x82\x3a\x94\x8c\x3d\xa7\x96\x31\x76\xba\x2b\x71\xfa\x2b\xf6\x9d\xbe\x82\x03\x97\x3b\xe0\xd0\xe9\x4a\x38\x72\x3a\x04\x8e\x5d\x11\x41\x8d\x37\xc6\x47\x89\x33\x5a\x60\xe6\xf2\x18\x9c\x5a\x82\x06\xce\x6c\x41\x36\x77\x7a\x2e\xe6\xce\xa0\x80\x0b\x6b\x44\xc4\x9e\x53\xeb\xd8\xe9\x88\x98\xb8\xbd\xdb\xb7\x58\x1a\x0e\x9c\x8e\x86\x43\x97\x0b\xe3\xc8\xea\x87\x38\x76\x46\x06\x4c\x9d\xde\x8f\x13\xa7\x2f\x62\x66\x09\x56\x38\x75\xba\x1b\xce\x5c\xd1\x01\xe7\x56\x2f\xc6\xdc\x19\x39\x70\xa1\x05\x87\x5d\xc6\x54\x2a\x06\x78\x62\x40\xd8\x08\x67\x18\x8f\xef\xb7\x8b\x1b\xc3\x70\x2c\xdb\x0d\x03\xb1\xc2\x67\x78\x14\x4a\x7c\xc4\xc8\x47\xd4\x3c\x34\x05\x61\xc5\x89\x79\x9c\xa1\x9e\x99\xff\xa4\xe9\xb7\x29\x04\x4b\x3e\x4d\x8f\xd2\x06\xa9\x81\xcf\xec\xbe\x3c\xec\x31\x0c\xbf\x66\x3b\xe1\x8d\x10\x0d\x6d\x0a\xc5\x84\xe1\x51\xbd\xa8\x64\xed\xb9\x7c\x8c\x5d\x32\x55\x30\xc4\xa5\x7f\x05\xe3\xbb\x74\xad\x7e\x0f\x5c\xc2\x56\x30\xa1\x5d\xac\x0a\x22\x1a\xed\x73\x6c\x31\x2d\xf5\x98\xba\x24\xaa\x60\x12\x9b\x96\xd4\x73\x66\xb7\x52\x05\x91\xba\xec\x51\xc1\x64\x66\x95\xab\xa7\xb9\xcb\x8c\x14\x0c\x77\x99\xa8\x82\x29\xec\x1e\x5a\x67\xc4\x46\xc7\xc6\xae\x1e\x60\x62\x11\x32\xf6\x6d\x16\x87\x03\x17\xb3\x38\x74\xa9\x05\x47\x2e\x61\xe0\xd8\xd1\x45\x5b\xfc\x4d\xec\x2a\xc4\xcc\x65\xa9\x38\x75\xc6\xc3\xcc\xe5\x51\x38\xb7\xdb\x37\xe6\x36\xa3\xc3\xc5\xb8\x77\xb5\x2f\x37\x56\x08\xec\x8e\x05\x98\x8c\x1b\x1c\xf6\xc7\xbc\x0f\x07\x4e\xef\xc3\xe1\x78\x10\xa8\x95\xed\xec\x6e\x3c\x1e\x94\x30\x1d\x0f\x6e\x38\x19\x8f\x06\xb5\x39\xb8\xbc\x4c\x1a\x85\xf5\x69\x36\x16\xd6\xa4\x61\x38\xf8\xe4\x63\x11\xa7\x36\x12\xa0\xa2\x8d\xec\xf2\xa3\x5e\xd7\xe0\x19\x5b\xbf\x5f\xa3\x6a\xc6\x2a\xb4\xe6\x73\x9e\x55\x50\x8f\xe8\xd5\x77\xcf\x5f\xa3\x72\x71\x51\x5f\x13\xd1\x54\x34\x78\xf6\xed\xab\xde\xc5\xc5\xed\xc1\xc4\x29\x6a\x37\xfe\xc3\x05\x8a\xea\x0b\x7c\x56\x5f\xa6\x7a\x43\x4f\xfd\x2a\x01\xe4\x97\xfa\xb3\xf8\x32\xd5\xfa\xd3\xe7\x5c\xab\xaa\xf4\xfd\xa3\x57\xb2\x30\x16\x92\x85\x5f\xdc\x77\x54\x09\xe8\xe6\x82\x2a\xf9\x45\xab\x92\x72\xd3\x2b\xaa\xdc\xa5\xf5\xde\xf3\xeb\xa6\x04\xd8\x7b\x7e\x6d\x28\x7d\xf7\x9e\x5f\xd7\x75\xf5\xde\xf3\x6b\x73\x59\x3d\x41\x43\xaa\x28\x8c\x50\x5a\x56\x6b\xc4\xb2\x6c\xb9\xca\xcb\xc5\x19\xaa\x96\xe8\xc5\x09\x36\xe2\xfd\xae\x84\x52\x40\x6f\xfa\x35\x90\x4d\x77\x87\x84\x91\xfd\xee\x90\x16\xdd\x8b\xa5\x40\xf8\xe2\x04\xbf\x29\xdf\xa2\x03\x84\x0d\x35\x4a\x15\x5d\x59\x9e\x7f\x52\xf7\xee\x4d\xdb\x5e\x95\xe3\x13\xff\x99\xf8\x18\x1d\x68\xa8\xa1\x0e\xdf\x1e\xba\x3b\x40\x6c\x28\x58\xfa\xed\x7a\xcd\xcf\xd3\x39\x47\x38\x42\xeb\xcb\xf4\x3d\xbf\x36\x88\x7f\x7d\x99\xfe\xc8\xaf\xd7\x8d\x0a\xda\xef\x76\xa1\x2c\x5e\x01\x90\x14\x4d\xfd\xe5\x21\xc2\x51\xf3\xcd\x7e\xc5\xca\x09\x54\x9c\x52\xfc\x98\x05\xb9\xae\xb1\x2b\x5e\xde\x28\xa4\x6f\x15\x53\x46\xbc\xee\xab\x5b\xd2\xb2\x7a\x05\x55\x51\x8e\xb5\x22\x28\x0d\x5e\x1b\x4a\x69\x50\x01\x35\x1a\x14\x19\xb6\x31\x59\x0d\x09\xec\x56\xd3\xa5\x53\xac\x96\xe7\x10\x60\xe6\xbc\xa8\x10\xa1\xe0\x19\x82\xb2\xb9\xa1\x14\xce\x9b\x49\x89\x8e\xe4\xdd\x10\x1e\x14\x70\xac\x8d\x6b\x32\x79\x71\x42\x94\x0d\xee\xa1\xfd\x46\x02\x7b\xe8\x4f\x88\xd0\xb7\x50\xe3\x11\x6c\xab\x44\x7f\x82\x3b\x2e\xb6\x66\x6f\x55\x9e\xcd\xb6\xe7\x2f\x80\xf2\x9d\x2d\x93\x7b\x1d\x2e\x09\x85\xc7\x92\x57\xb4\x8f\x48\x60\x61\x78\xcf\xc0\xf1\x80\xac\xa9\xb2\xbf\xe8\x40\xb9\xc8\x38\xe2\x2c\x9b\x29\xb3\x43\xe5\x1a\xb1\x8b\x8b\x79\xc9\x73\xa1\x4b\xb6\x40\x7c\x73\xc1\x16\x39\xcf\xeb\xba\x8c\x10\xde\xa7\x46\x6c\x42\x04\x0a\x4d\xc6\x16\x28\xe5\x28\x5d\x2d\xdf\xf3\x05\x2a\x17\xd5\x12\x51\x59\x14\x78\x8d\xd6\x19\x9b\x4b\xf4\x12\xe5\xda\x8c\xed\x6a\x56\x66\x33\xc4\xe6\xf3\xe5\xd5\x1a\x50\x0b\xbc\xd5\x52\xa0\xbd\x5c\xf3\x1c\x5d\x95\xd5\x6c\x79\x59\x49\x06\xd7\xe5\x72\x31\xc4\xa2\x04\x0d\xe5\x35\x27\xed\x97\x87\x0f\xd5\xb5\x32\xed\x4f\x22\xa0\xf8\xd8\x24\xb9\x8e\xe5\x62\x69\xb9\xb1\xdb\x70\x15\x5a\x08\x62\xed\x67\x88\x59\x93\x52\x2a\xf1\x6b\x24\xb4\xef\x9b\x55\x65\xeb\x47\xac\xf7\x23\x7e\xab\x0a\x7b\xfe\xaa\xff\x04\x97\x02\x0c\xae\xda\x31\x44\xc0\x13\x59\xf8\x12\x95\x8b\x0f\x7c\xb5\xe6\xf6\x28\x58\x2e\x3e\xbc\xea\x05\xc2\xce\x4f\x5b\x0d\x10\xd8\x31\x40\xb4\xd8\x74\x89\xad\xdf\xe0\x50\x18\x74\x1f\xfb\xc7\xce\x84\x43\xfb\x85\x2f\xb2\xd5\xf5\x45\xb5\xc3\x55\x80\xaa\x62\xed\xf2\xa4\x69\xd7\x02\x4f\xbb\x21\xdf\x5a\x42\x37\xe7\x9f\x83\x6a\x2b\x11\x57\xed\xde\x13\x37\xe5\x69\x2d\x48\x53\xd2\xf1\x1f\xbc\xd2\xf3\xb4\x2e\x73\x73\x40\xaa\x5d\x8d\xd5\xd7\x81\x04\x5b\xf5\xc1\xe0\xe6\x2c\x43\xf6\xf1\xc3\xa2\xac\x4a\x36\xd7\x4b\x5f\x75\x61\xf8\x26\x9b\xb1\xc5\x19\x7f\xfa\xb2\x2d\x8b\x2a\x2b\x8f\x79\x1b\xaf\x90\xff\xeb\x9b\xb4\xb9\x8d\xbc\x9f\x1a\xde\x58\x8b\xc2\xda\xe6\xe5\x53\xbd\x0d\x01\x3a\xbe\xfa\xdb\xae\x0d\x95\xbc\x79\x45\x21\xfe\xbf\x25\x6f\xd0\x26\x54\x7f\xc6\xca\xb4\xae\xab\xda\x64\xf9\x30\xf0\x28\xf9\x51\x7a\x15\x7c\x1e\xbf\xb6\xcd\x30\x12\x19\xf3\x09\x40\x67\xbb\xf6\xa2\x31\x0c\xdd\x4e\x2c\xb0\xab\x2e\xec\x4a\xc1\x1a\x99\x7c\xcc\xcb\x75\xc5\xe7\x8d\x15\x9b\x31\x16\xd0\xf9\xed\x52\x0b\xea\x0e\xd0\x85\x18\x68\x65\xa9\xb5\x37\xe5\xdb\x37\x93\x89\xe2\xf6\x5d\x1b\xae\x45\x22\xd9\xbc\xba\xc0\x77\x28\xab\x6d\x12\x8d\x21\x60\xf7\x1c\x69\x65\x93\x54\xcf\x93\xe6\x35\x1b\xc5\x78\x00\xff\xf3\x22\x5f\xa2\xf5\x15\xbb\x90\xe9\xc7\x9c\xad\x2b\x69\x0c\xc3\x10\x5e\xb9\x55\xd6\x63\xb6\xab\x30\x97\xe3\x57\x06\x1b\x86\x8a\xe2\xbb\xba\xfa\xc0\x35\x6e\xcd\x05\x6f\xe2\xea\x37\x09\x29\x23\xa1\xcb\xf0\x46\x56\xa1\xe5\x65\x35\x88\xc0\x4d\xc8\x75\xab\xac\x13\x72\xed\x3a\xeb\x0c\x19\xef\xf9\xb5\x2c\x01\x1d\x05\x47\x3e\xd1\x9f\x94\x1f\x2c\x0f\xb4\xba\xd1\x91\xb1\x6a\xf4\x11\x7a\x25\x2c\x50\xbd\x04\xac\x96\xeb\x75\x9b\xa6\x43\xcd\x43\x48\x88\xe1\xb5\x54\xb6\x68\x06\xaa\x56\x70\x93\x7a\xbc\x3a\x67\xeb\xf7\x1d\x97\xad\x6d\x77\x32\xe9\x98\xa8\x70\xc4\x7a\x74\x7d\xd7\xe9\xba\x70\x5a\x81\x45\x13\x41\xc7\x64\xdf\x81\xcd\x7e\x65\x34\x7c\xf1\x4c\x64\x54\x12\xb3\x82\xaa\xfd\x6e\xc0\xf6\xcb\xa7\xdb\xb3\xbd\xb2\xb3\x3d\x77\xb3\x3d\x77\xb0\xbd\xda\x82\x6d\x67\x11\xe9\x75\x5d\x45\x5a\x4e\x7f\x6c\x57\x47\x7a\xac\x08\xb3\xc4\x55\xf1\x4d\xa5\x97\x62\xfe\xfe\xd1\xab\x43\x95\xa0\x75\x6a\x31\x4f\x51\x56\x9c\x19\x8a\x6b\x5f\xcc\x99\x60\x62\x53\xa1\x3e\x16\x95\x70\x4d\x5a\x3a\x26\x44\x4d\x65\xe7\xe1\x44\x4d\xb7\xe8\xf6\xf7\x8f\x5e\x19\x2b\x6e\x9f\xae\xca\x8b\x39\x3f\xd8\x6d\x8a\x48\x36\xea\x4c\x14\xe9\x3f\xfd\x7e\xa6\x8b\xd4\x44\x84\x60\xbb\x84\x0a\xa5\x59\xff\x7a\x20\x95\xc5\xf2\x35\x46\xc7\x02\xee\x50\x4a\xf5\x91\xd4\xf1\x72\x35\x69\xef\x59\x57\x17\xc7\xd7\xa4\x0f\xd7\xf3\x32\xe3\x13\x6f\x8a\xc8\xde\xe0\x2e\x8c\x06\x2d\xb9\x21\x5a\x32\x45\x81\x03\xad\x7f\x43\xb4\xc1\x14\x45\x7b\xf6\x8b\x34\x6e\xfc\xee\xc1\xd7\xf8\x50\x6f\xac\xb5\xb0\x4a\xe6\x50\x7f\xe7\xd8\xa2\x81\xbf\x05\x85\xdb\x79\xa7\x11\xb4\x76\x64\x8e\xec\xda\x7d\xbc\x05\x05\xf3\xa8\x87\x13\x72\x6b\xc3\xde\x3f\x49\x58\x6d\xa2\xcb\x2d\x04\xd7\x16\xd7\x8e\x21\xd6\x16\xe2\xba\x81\xb6\x81\x72\xd6\xcf\x6f\xa0\x7a\x25\xf4\xb5\xc2\xec\xf7\x43\x32\xed\x55\xd5\xd7\x8a\xbb\xdf\x0f\x83\x69\x5b\xd5\xfd\x7e\x18\x4d\x55\xb1\xf7\xfb\x11\xfe\xf8\x76\x4a\x83\x4f\x2a\xb8\xff\x5b\x56\xda\xff\x6c\xf5\xf0\xff\x7b\x2a\xdb\xc3\x4d\x05\xe5\x82\xe7\xb7\x5b\xe2\xfe\x3b\xb6\xe6\x6d\xd5\x7a\xb6\xe6\xda\xb3\xd7\x3e\x71\x56\xc0\x1f\xfa\xf2\x26\x0a\xd0\x82\x9d\xf3\xf5\x85\xee\xa5\x47\x3a\x1b\x02\x44\xb0\x21\xff\xfb\xf7\x8f\x26\x34\xdf\xa2\x28\x68\xae\xb0\x31\xa1\x79\x1d\x05\x82\x0f\x60\x6a\x13\x05\x87\xea\x8b\xe0\xdf\x90\x19\xb4\xa8\x25\x7a\x35\x9d\x52\xfe\x8d\xaf\x11\x43\x0b\x7e\x35\xbf\x46\xd2\xd7\x72\x13\x61\x3d\xa0\xa0\xce\x6d\x1e\x8b\xcb\xf3\x94\xaf\x3e\x22\xb8\x55\x0a\x6e\x55\x11\x1f\x7c\x02\xe9\xfc\xa1\xb3\xc9\x7c\x79\x05\x2d\xc4\x7f\x4d\x0d\xba\x8d\xbb\xd1\x6d\x08\x50\xcb\x65\xd3\xca\xa5\x8e\x08\xb5\x78\xea\x81\x59\xae\xfe\x79\xc4\xf3\xe1\xad\x2c\xf0\x42\x2f\xf2\xba\xf3\x9d\xb5\xa4\x21\xc4\x2f\xca\x4e\x46\x25\x7a\x38\x15\x5c\x9b\xc7\x30\x75\xbf\x96\xe1\x56\x4f\x78\x2c\x7a\x7b\x8c\xba\xb7\x6f\xeb\x6f\xe6\x7d\x4d\x7d\x57\x56\x57\xe5\x9a\xa3\x9f\x9e\x9f\xae\x01\xc3\x98\x62\xea\x8b\x52\x94\x81\x7c\x44\xdf\x0a\xfd\x0a\xb9\x1c\x80\x60\xd4\x48\xc2\x8a\x8a\xaf\xd0\x82\x9f\xb1\xaa\x5c\x9c\xdd\x82\xe0\x01\x15\x17\x82\x57\x2a\x38\x5c\x2c\xab\x89\x55\xaa\x47\x47\x68\xb1\x1c\xcd\x54\xe1\x4e\x16\x29\xd0\x7f\x34\xd2\x7d\x60\x04\x93\x82\xfd\x47\x2d\x64\x43\x4a\xaa\x24\xa3\x04\x53\x5b\x43\xab\xce\x07\x1d\xee\x3a\x19\x80\x4d\x2b\xdf\xfe\xf4\xbd\xa6\x15\x58\x4e\x80\x71\xfb\x82\xad\x61\x79\x61\x2b\x1f\x6a\x34\x05\x38\x84\x4b\x34\xca\xaa\x96\x82\x44\x8d\xf7\x96\x95\xff\xed\x4f\xdf\xdf\x8e\xea\xe5\xda\x4e\xab\x78\xb6\xc8\x27\x6c\xb1\xac\x66\x7c\xa5\x18\x71\x99\x01\x5b\xe4\xba\x19\x88\x1e\x8e\x98\x42\xeb\x67\x77\xa5\x40\xc6\xac\xa2\xf1\x3c\x05\xff\x9b\xd9\xc7\xf3\x97\x9f\xdb\x3c\x9e\xbf\xfc\x4c\xd6\xf1\xfc\xe5\xed\x18\xc7\x72\xd5\xb1\x8d\xe5\x6a\x07\xd3\x58\xae\x6e\x6c\x19\xbf\xee\x68\x19\xbf\xfe\xc6\x96\xf1\xfa\xf3\x9b\xc6\xeb\xcf\x66\x1b\xaf\x6f\xcb\x38\x36\x3d\xeb\xd8\xec\x64\x1e\x9b\x4f\xb0\x8f\x77\x3b\xda\xc7\xbb\xdf\xc8\x3e\x60\x51\x5e\xb7\x8c\x85\x9c\x19\x55\x2f\x84\x73\x5e\x54\xdb\x67\x65\x0b\xb0\x09\xf9\x0d\x2d\x8b\x06\x13\x5c\x61\x73\x5b\xc6\x00\xc8\x6e\xc7\x1c\x00\x55\xc7\x20\xe0\x97\xa7\x13\x12\xba\xec\x40\x02\xe9\xa6\xb0\x30\xd9\x81\x78\x05\x5a\xa0\x87\xc8\x27\xb6\x95\x2e\xcd\x52\x26\xad\xa9\x3c\x7c\x88\x16\xb0\x44\xde\x18\x83\xdc\x3a\x44\xd0\x01\x5a\x18\x2f\xab\x37\x9b\x90\xc0\x33\xb4\xb5\x8f\xa8\x7e\x79\x72\x33\xa4\xa3\x99\x2c\xd0\x81\xe1\xc6\xd0\x01\xe9\xfe\x52\x97\x20\xf7\xdf\x69\xbd\x30\x95\xff\x6f\x67\xbe\x2f\x27\xf6\x97\x8b\xda\x7a\x5f\xde\x92\xf5\x4a\xbd\x77\x2d\x55\x33\xde\xda\x9e\xb7\x30\xde\x41\xc4\x04\x54\x37\xb0\x5f\xcd\x0b\x1a\x3c\xe3\x06\xac\xc8\xff\xe6\x16\xfc\x72\x59\xb1\x8a\x7f\xee\x00\xbc\x02\x2a\xb7\x65\xc2\x80\xed\x76\x4c\x58\x32\xa6\x9b\xf0\x6a\x39\x1a\x7f\x05\xc8\xa8\xfd\xaa\x1e\x81\x1d\xa8\xa8\xbe\xd8\x13\xe9\x60\xfb\xcb\xcb\x49\x14\x0c\xcc\xf2\x53\x15\x76\x4b\x31\xe7\xf7\xa5\xb1\x91\x90\x23\x20\x76\x57\xd8\xcb\x81\xc2\x9e\xde\x44\x61\xdf\xe6\xf9\xe7\xce\x7c\x59\x9e\x7f\xa6\xcc\x57\x5e\xf9\x7d\x1b\xef\xcc\x79\xef\x9d\x39\xdf\xe9\x9d\x39\xdf\xfa\x9d\xb9\x3f\x22\xec\x37\x89\x2c\x6c\x18\x35\x27\xbf\x19\x5b\xad\xae\x45\xb3\x7a\x0c\x91\x17\xc3\x77\x86\x95\xf6\x7a\x78\x33\x8e\x61\x22\xb5\xdf\xe6\xdc\x68\x5f\xd2\x50\x3c\x7c\x6a\x44\x97\xdf\xcc\xab\x2b\xdf\x2e\xd4\x15\xe0\xcb\x42\x9f\xdb\x5c\x9b\x6e\x38\x5e\x2d\x2f\xf8\xaa\xba\x46\x7f\x57\x57\x0c\x03\x20\x98\x57\x83\x62\x30\xad\xa8\x0c\x64\x7d\x68\xc2\x53\x87\x95\xe6\x4e\xf4\x6e\x74\x59\x97\x67\x8b\xb2\x28\x33\xb6\xa8\x50\x0a\xcf\xcb\x85\xe6\x1b\x40\xd4\x31\xfb\xdb\xce\x4b\xd7\xcc\xd4\xbf\xdc\xc2\x3c\xf0\x90\x03\xbb\x3b\x76\xc4\x35\x79\x7e\x21\xcc\x92\xcd\xf7\x3a\xb2\x1f\x15\x1c\x32\x06\xe4\x46\x72\x1a\xda\xad\x84\xc8\xbb\x6a\xfe\x04\x5f\xbd\xd2\x45\xdd\xef\x45\x67\xcd\xb7\xeb\xb3\x9f\x88\xec\xcd\xa0\xbd\xf8\xdb\x75\x5a\x7b\xba\x2b\x16\x4c\x71\x82\x19\x4e\xe1\x4c\x4d\x86\x73\xcc\x71\xb1\x37\x40\xf2\xf6\xdf\xa8\xab\x53\x84\xbd\xad\x97\x07\xc0\xe8\xa6\x8d\xd9\x0e\xc2\xf2\x95\xda\x3c\x01\x61\xb1\xfe\x22\xff\xfb\xeb\xaf\x86\x03\x18\x22\xef\x6f\x7c\xe0\x0f\xc7\x68\xb8\x0a\xa6\xff\xc9\xb1\xb9\x06\x3f\x6e\xd8\xe8\xef\x05\xb4\x26\xed\x7d\x04\xd2\x87\xe6\x7c\x71\x56\xcd\xd0\xd7\x88\x6e\xb9\x95\xba\x1f\x68\x4e\x96\x8b\x0f\x7c\x55\xbf\x1a\x6a\x61\x58\xc5\x07\x31\x68\xd7\xa7\x03\xb6\x0a\x3c\xf5\xa8\xdd\x68\xb7\xb3\x32\xf7\x11\x9d\x76\x83\xe8\xbd\x35\xca\x59\xc5\x10\x5b\xef\x48\x67\xeb\x99\xac\xee\x4a\xe1\x46\x0b\xd0\x87\xd5\xf2\xb5\x4f\xec\x4b\x21\xf0\xf8\x13\xf6\xec\x28\x5a\x5d\xa3\x32\xec\xdc\xa9\xe1\x9e\x4a\x65\x36\x4c\xd6\xea\x35\xed\xe2\x91\x6a\x33\xe0\x92\xdd\xdd\x7a\xf3\x7e\x97\xb6\xfb\xa4\x57\xbb\x84\x57\xb7\x7a\x33\xd8\xc2\x2f\xfe\x6a\x1e\x0e\x2f\x2e\xd7\xb3\x49\x9d\x48\x89\x1c\xc1\xf4\x5e\x69\x86\xee\xe5\x12\xc8\xb0\x4f\xb6\x4e\x45\x34\x05\xd7\x11\xa4\xc6\x39\xed\xba\x8d\x75\x23\xc9\xc0\x2b\x00\x8d\x30\xc9\x6c\x79\x01\x83\xa4\x65\xec\x47\xa3\x69\x6b\x63\xf6\x1c\x65\xf3\xe5\xc2\xf5\xa6\xb2\xad\x49\x03\x9e\xbe\x2d\xc3\x8f\x76\x5b\x86\xc7\x4e\x5b\xd6\x31\x43\x96\x22\xd9\x6d\x76\xbe\x9a\x76\xba\x9e\x00\xfc\x1f\xc1\xb0\xff\x28\x25\x33\x44\x5a\xc7\x52\x89\x6f\x18\x66\xeb\x5d\x63\x76\x02\x70\x86\xa9\x5e\x58\x97\xc9\x89\x85\x4c\xe3\x42\x57\x1d\xff\x19\x75\x83\xab\x6d\x7c\xe0\x4a\x99\x7c\x8d\xfe\x4d\xf9\xd6\x24\x76\xbb\xa9\x02\x70\x67\x7d\xb9\x49\x8f\xad\xfb\x66\x7a\xbb\x65\xd4\xd6\x98\x8f\x6f\xa7\x34\xdc\x66\xbf\xcb\xd1\xd7\x7f\x40\xb3\xaa\xba\x58\xdf\x3f\x3a\x3a\xaf\x66\xeb\xc3\x94\x1f\x5d\x56\x05\xfd\x65\x8d\x3e\x90\x43\x7c\x48\x50\x7a\x8d\xfe\xe7\x39\xab\x66\x25\x5b\x0b\x8b\x69\x37\xc8\xc0\xae\x10\xb9\xd9\xe3\xe8\x08\x7d\xcf\x2b\x79\x1c\x8e\x73\x21\xee\x92\xa5\x73\xbe\x46\x7f\x55\x94\xfe\x7a\xe7\x2b\xd8\xc6\xbf\xe2\xfc\x51\xb3\xff\x65\xb0\x93\x06\xdd\x93\xca\xbb\x87\xee\xde\xad\x7f\x7e\x60\x47\x8f\xfe\x2a\xbb\xa3\x21\x7f\x06\x3f\xb4\xb8\xcf\xd5\xf7\x2e\x6a\xf5\xeb\xdd\xbb\x86\xfd\x39\xc7\x1d\x26\x1b\x60\x27\x1b\x67\xb0\x73\xe6\xaf\x53\xb9\x1b\xff\xa7\x65\xce\x0f\x7f\x59\xa3\xe5\x0a\x7d\x27\xb7\xd2\x94\x45\xc9\x73\x94\x2d\x73\x3e\x05\x2c\x6c\x91\xa3\xcb\x35\x47\x65\x25\xc6\xb5\xbf\x0a\x39\x6a\x7d\x50\xfb\x70\x9a\x3e\x9c\xa9\xef\xdd\x3e\xc8\x5f\x1f\xc8\x3d\x49\x6d\xb3\xc3\x06\xfa\x58\x47\xf6\xeb\xaf\xda\xb7\xc3\xab\x72\x91\x8b\xb7\xcb\x0e\x8c\xdc\x3a\x24\x78\x41\xfa\xcf\xb0\xd9\xe7\xce\x57\x47\x5f\x1f\xdc\xda\xdf\xd7\x47\x77\x64\x6f\xd7\xd5\xaa\x5c\x9c\x3d\x5e\x2d\xcf\x4f\x66\x6c\x75\xb2\xcc\x85\xe6\x5e\xc1\x8f\x87\x85\xf6\xab\x12\xfe\x29\x7b\xcf\x17\x52\xc6\x7d\x93\xbd\xb8\x5c\x5c\x0b\xf9\xde\xf9\xaa\x89\x60\x97\xd9\x9a\xe4\x5c\xfc\x38\x91\x74\x64\x07\x61\x69\x13\x36\xdf\xd7\x43\x20\xfc\x94\x2d\x2f\x17\x15\x5f\xa9\x99\x4b\xf8\x69\x5e\xc7\x0a\xd9\xbc\x0d\x16\xf0\x14\xce\x33\xd6\x5f\xf8\xa6\x5a\x31\xf1\xe5\x6a\x56\xce\x39\x9a\xd4\xd8\x1e\x2a\x24\x92\xf4\x57\xd0\xa6\x45\x98\xa9\xee\x7d\x5b\xd5\x0d\xf6\xf7\x85\xab\x7f\x05\x3a\x95\xc0\xdf\x1c\x23\x6f\xf3\x3d\xf5\x3c\xa1\x73\xf9\xd3\x43\xf8\xe9\xbb\xc7\x8f\xc5\x4f\x16\x4a\x42\x5c\xf0\xba\xbe\xbe\x5c\xad\x96\x67\xac\xe2\x53\xb0\xba\x6a\xc6\x57\x1c\xce\x79\xa2\x05\xdf\x54\x48\xb0\xc0\xb2\x8a\xaf\xa0\x11\x74\x63\x1b\xfe\x80\xc1\x89\x04\xbf\x8b\xbc\xcd\xe3\x13\xcf\xdb\x13\x16\xea\x6d\xbe\x87\x8f\x7f\x17\xc1\x79\xbe\xbc\x6a\xe9\x43\xb3\xaf\xa4\xe4\xe5\x50\x3e\x51\x5d\x14\x08\xfc\xc7\x8f\xf7\xe0\x68\xa6\xb7\x87\xf6\x91\x86\x19\x1e\xec\xd7\x15\x87\x14\xf5\x36\x0b\x56\x5d\xbd\x5c\x9c\xb3\x2a\x9b\xf1\xbc\xa5\xf7\x00\x2d\x17\xf3\x6b\xc4\x2e\x2e\x38\xf4\xbb\x5c\x83\x03\xa2\xcb\x45\x59\x4d\xc5\x8b\x66\xc6\xd6\x1c\xde\x36\x85\x20\x1a\x4c\x0d\x8c\x10\x52\x55\xef\x8b\x6a\xb0\x8a\xa1\x9e\x69\x5f\x2f\x58\xb9\x1a\xf6\x0c\xfa\xa5\x78\xfd\x4a\x89\xee\xe0\x40\xf1\x7e\xa7\xdf\x01\x4b\x4b\x01\x28\xfe\xaf\xe2\xbd\x84\xaa\xbd\xf1\x26\xce\xc0\x17\xe0\x0c\x30\x0a\xb7\xbe\xd0\x58\xb9\xcc\x5b\xba\x46\x5e\x2e\x72\xbe\x41\xc7\xe8\x00\x1b\xcd\xbe\xf1\xa3\x7b\xf7\x34\xe3\xdf\xdf\x97\xcd\x2c\xc6\x0f\x74\xde\x00\xc8\xdb\xbe\xb1\x0b\x53\x7a\x2c\x34\x2e\x25\x23\x7f\x3d\x38\xae\xd5\xff\x40\x93\x17\xda\x3f\x36\xc4\x8f\x1a\xd1\x37\xdf\x20\xec\xd5\x06\x84\x7e\x55\x3e\xa4\x54\x52\x73\x22\x8d\x15\xfd\x8a\x3a\x76\xd8\x08\x7f\x0b\x42\x80\xd0\xa6\xa4\x46\xf8\xd9\x8c\x67\xef\x5f\x65\x6c\xce\x56\xff\x5b\xb4\x9a\x08\x3d\xbc\x58\x96\x0b\xb9\x9b\x1a\x04\xd0\xfc\xd4\xf5\xf8\xf6\x67\xe9\xf5\xad\x70\xaa\xd9\x6a\x79\x85\x1e\xad\x56\xcb\xd5\x04\x7a\x75\xef\xa9\x48\x85\x5a\xd3\xfc\xf3\xfe\x3d\xb4\xdf\x22\x38\xac\x96\x32\xb2\x4e\x70\xb4\x77\x58\x2d\xff\x7c\x71\xc1\x57\x27\x6c\xcd\x27\x7b\x68\x5f\x22\x10\x26\xbf\x58\x56\xc2\xc0\x81\x59\x29\x97\x7b\xe2\x61\xdd\xd1\x8f\x9f\x61\x24\x68\xe5\x04\x59\xb5\xc8\xc4\x5b\x71\x4c\xe5\x32\x9b\x1a\x9c\xa4\x94\x0d\xda\x98\xe8\x02\xfc\xa6\x6e\x23\x35\x0a\x53\x95\x1b\xea\xed\xf5\xf5\x22\x1d\xe2\xa4\x6e\x68\x52\x8b\x86\xf6\xae\x32\xce\xc7\x8f\xa9\x8a\x75\x2a\xcc\xe1\x83\xf4\xba\xe2\x68\xcd\xff\xeb\x92\x2f\x32\x08\x74\x76\x46\x5b\x1a\xb5\xe9\xc0\x40\x78\x7d\x9e\x2e\xe7\x8d\x23\xd9\x28\x53\xaf\x4b\x99\x0c\x29\x37\x98\xc6\x85\x14\x49\x01\x61\x25\xa0\x13\xaf\x61\xa9\xd9\x78\x6c\x60\x02\xc2\xb0\xce\x84\x3f\x64\xc2\x61\xf0\x0f\x76\x64\x12\x13\xc9\xa5\xa7\xb8\x7c\xe4\x75\x50\xec\x1f\x5b\xac\x26\xda\xa2\x33\x8f\xbc\x41\x67\x82\x4f\x92\x28\xa6\x8a\xd9\x58\x32\xfb\x78\x4b\x66\x31\xd9\xb5\x53\x2d\xa4\x89\xab\x6e\x47\xbb\x1e\xd0\xd8\x26\x60\xe8\xbb\x84\x48\xfd\xd5\x38\xd1\x4f\x9a\x1a\xa4\x22\x75\x1f\x26\x57\x83\xac\xa9\x85\x1f\x1d\x54\x1a\xd0\xfa\x07\xa1\x04\x19\xad\xb6\x1c\x5c\xda\x1e\xeb\x84\xf5\x51\x46\x43\xb9\x7f\xec\x70\xfd\x5e\x44\x6f\x9b\x7d\xae\x44\xb8\x91\xfd\x8a\xb3\xfc\x64\xb9\xa8\xca\xc5\x25\x1c\x9e\x05\xed\xb7\xa1\x48\x70\xf2\x03\xf4\xfd\x9b\x63\x60\xeb\x44\x24\x16\x86\xd1\xe0\xde\x0f\x8b\x0f\x6c\x5e\xe6\x00\x24\xa5\x7d\x4f\x75\xab\x91\x77\x97\x0a\x92\x08\x61\xa2\xe0\x4d\x43\xe7\xad\x72\x13\xd1\xb4\xf9\x71\x7f\x5f\x24\xe3\x75\x84\xea\xa1\xb9\x2b\xc3\x88\x4c\x04\x45\x94\xfc\xbb\x16\x0c\x8d\xd0\xfe\xe3\x86\xb1\xa3\x23\xf4\x43\x81\xae\x38\x12\xf9\xda\xe5\x05\x12\x99\xea\x14\x95\xd5\xff\xfb\x3f\xff\xb7\x1e\x96\x74\x14\xc0\xf1\x1d\x4b\xcf\x07\x80\xf7\x06\xc1\x5f\x5a\xef\x2b\xf0\x82\x49\x6b\xe5\x02\x18\xeb\x66\x48\xf4\x2f\xbe\xfe\x25\x30\x98\xef\x50\x57\x9f\xa0\xaa\x2e\xa6\xe3\xa1\xd6\x95\x64\x0b\x36\x87\xc3\x0f\x8d\x1c\x5f\x72\x96\xa3\xa2\x5c\xad\xab\x5a\x4a\xd0\xad\xdd\xd5\x3c\x1c\xdd\xd0\x64\xb1\x1c\x8a\x77\xbd\x57\xdb\x84\x24\x74\x57\xe9\x5f\x45\x56\x8d\xd7\x46\xbe\x35\xaf\xc3\x31\xac\x87\xe7\x51\x6d\x50\x27\x35\x2a\x50\x0b\x3a\xb6\x38\xcc\x83\x7e\x3c\xd0\x91\x61\xf9\x9a\x01\x35\x77\x1a\xed\x9a\x12\xb0\xc6\x7a\x5b\xf3\xd5\x62\x54\x37\x81\xdf\xc1\x04\xeb\xb4\x5e\xf6\xdd\xef\xcb\xf6\x9c\x5d\xa3\x72\x91\xcd\x2f\xe1\x25\x44\xbc\x5c\xe8\xaf\x34\x26\x29\x3f\xae\xa5\xf3\x68\x07\xe9\x80\x29\xdf\x4c\x80\x9e\x7a\x4f\x23\xb0\x37\x49\xd2\xd2\x05\xea\xdb\x04\xea\x41\xf2\x22\x05\x36\x96\x1f\x7c\x4e\x99\x0f\x47\xf8\xbe\x44\xa9\x92\xe8\xe3\xdb\x95\x28\x84\x8c\x1b\x0a\x3d\x06\xa1\x7b\x9b\xbe\xd8\xbd\x8d\x77\xb2\x87\x7e\x05\x89\x4c\x24\x0f\xf2\xd7\x46\x1f\x81\x55\x1f\xf0\x46\x65\x78\xc7\xc0\x9e\xfe\x0a\x66\xd6\x44\x2d\x4f\xa3\x16\xfe\x7c\xfa\xf8\x80\xa2\x1c\x66\xca\x78\xde\x44\xde\x3a\x6c\xaa\x13\x58\xcd\x77\x08\x68\xda\x77\x88\x3f\x0f\x7a\x39\x89\xca\x35\xda\xd1\x58\xf2\xd7\xe0\xeb\xa6\x24\x1a\x58\x1d\xd5\x80\x8a\x1e\x00\xb5\xa4\x44\x8b\xb1\xed\xec\x4f\x27\xdd\x69\xe7\x89\xaa\xf3\x0b\x2d\x1b\x99\x54\xe7\x17\xe8\xb8\x37\x96\xec\xa1\x3f\x1c\x1f\xcb\xa0\xdc\xcf\x4e\xd4\x22\x46\x75\x7e\xd1\xcf\x33\xb4\x17\xf4\x16\x7a\xef\x73\x4e\xbe\x09\xb1\xa2\x63\x60\xf0\xde\x07\xbe\x5a\x97\xcb\xc5\xbd\xfb\xe8\x1e\x4c\xfa\xde\x9b\x8a\x5f\x25\x3f\xf7\xee\x6b\x59\x21\xfc\x2e\xbb\xab\x7e\x97\x5f\xee\x7c\xf5\x51\x4d\xd2\xbd\x5a\x9e\x73\xf4\xed\xb3\xef\x51\x7a\x59\xce\x73\xb4\xbc\xa8\xca\xf3\xf2\x6f\x7c\xb5\x9e\xa2\x79\xf9\x9e\xa3\xd5\xe1\x2f\xeb\xa9\x7c\x25\x86\x99\xf6\xf5\x05\xcf\xca\xa2\xcc\x84\xf3\xe6\x25\x28\xfc\x82\x55\x15\x5f\x2d\xd6\x80\x0f\x1a\x55\x33\x8e\x8a\xe5\x7c\xbe\xbc\x2a\x17\x67\xf7\xe5\x9c\xa7\x30\xbf\xde\xb9\x48\x74\xaf\x36\x9a\x7b\x72\x72\xb7\x03\x70\xc8\xce\xf3\xde\x2c\x6a\x73\x44\x52\x3c\xbb\xf3\x95\x54\x97\x3a\x34\xd9\x4c\x73\x77\x07\x30\xd1\x67\xd0\x1d\x28\xa7\x7d\xbb\xe8\xcd\x1a\xff\x41\xfb\x7e\xb8\x58\xe6\xfc\xf4\xfa\x82\xb7\xc9\x5c\x3b\x57\xad\x5e\x3c\xca\x85\x3e\x6f\xfc\xb2\x5c\x9c\x2d\xff\xd7\x2b\xf4\xc1\x3b\xa4\x87\x1e\xbc\x9e\xb7\x2d\xb4\xb3\xa4\x0d\x33\x2a\x34\xd6\x98\xd8\xea\x6a\xc6\xe6\x3d\x4c\xf1\xa1\x77\x20\x27\x62\x56\xf5\xde\x28\x79\x8a\x51\xfd\x36\x63\xeb\xe7\x57\x8b\x17\xf5\x16\x98\x63\x05\x74\xd8\xfd\x1d\xc0\x9b\x25\x12\xa8\x1a\x27\x85\x52\x47\x8c\x2e\xb8\x5c\x1f\x12\xcf\xe1\x20\xf1\x9e\x90\x8d\x2e\xab\x37\xef\x65\x01\x43\x01\x01\x9f\x3b\x93\x5f\xbd\x7e\xbd\x9c\x95\x8b\xa5\xe8\x15\x43\x57\x3c\x45\xea\xa0\xaa\x9a\xb5\x3e\x54\x06\xad\x64\xf2\xf1\x8e\x3a\xa2\x0a\xcb\x26\x1f\xa7\x7f\xff\xf8\x76\x4a\xa3\x6d\x96\x44\x06\x27\x76\x5f\x3f\x7b\xfa\xa4\xaa\x2e\x5e\x8a\x21\x63\x5d\x35\xd8\xfe\x98\x96\x67\x72\x33\xcb\xe1\x2f\xeb\x3f\x6e\x83\xf9\xde\xe5\x9a\xc3\x0b\x5b\x56\xdd\x7b\x70\x67\x48\xe8\xbb\xf2\xec\x27\x40\xf8\x40\x74\xf8\x97\xf5\x4c\x04\xe5\xf2\x6c\xb1\x5c\xf1\xfb\xf3\x72\xc1\xef\x34\xa4\xaf\x78\xea\x6f\x45\x52\x28\xe9\x67\x9e\xca\xb1\x49\x1e\x33\xbe\x77\x78\x34\x2f\xd3\x23\x81\x42\x04\xe7\x3b\x47\x47\x28\x5f\x2e\xee\x55\x68\xf9\x81\xaf\x56\x65\xce\xeb\x15\x87\x7a\x81\xe3\x8e\x76\x06\x59\x2d\x1d\x88\x08\x77\xaf\xd9\xd1\x00\x0b\x12\x1d\x80\x43\x49\xb3\x0b\x25\x2c\x04\xd6\xc9\x74\x10\xe0\xee\xc1\x9d\x8f\x06\x71\xc8\x27\x6a\x65\xab\x66\xf9\x8f\xf7\x09\xf9\xf8\x56\x88\x61\xfa\x46\x8a\xe1\xed\xde\x9d\x3b\xff\x3f\x00\x00\xff\xff\x30\x1c\x67\x1d\x64\x21\x06\x00")
func web3JsBytes() ([]byte, error) {
return bindataRead(
@@ -106,7 +106,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{0x1, 0x32, 0x6b, 0xa6, 0x48, 0xcf, 0x60, 0x55, 0x11, 0x99, 0x68, 0x24, 0xdb, 0x89, 0x36, 0xbe, 0xaa, 0xc, 0x77, 0xc9, 0x80, 0xa8, 0xd2, 0x72, 0x9d, 0x71, 0xa8, 0xc3, 0x9a, 0x61, 0x8f, 0xaa}}
+ a := &asset{bytes: bytes, info: info, digest: [32]uint8{0x4c, 0xb2, 0xb5, 0x69, 0x9e, 0x5e, 0xe3, 0x56, 0x47, 0x6, 0x96, 0x4a, 0x7a, 0xcd, 0xdf, 0x79, 0xc5, 0xf2, 0x71, 0x64, 0x77, 0x8a, 0x53, 0x58, 0x6, 0xb1, 0xff, 0x4f, 0x0, 0xd6, 0x7f, 0x61}}
return a, nil
}
@@ -202,10 +202,12 @@ func AssetNames() []string {
// _bindata is a table, holding each asset generator, mapped to its name.
var _bindata = map[string]func() (*asset, error){
"bignumber.js": bignumberJs,
-
- "web3.js": web3Js,
+ "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
diff --git a/internal/jsre/deps/web3.js b/internal/jsre/deps/web3.js
index 950576f4d9..c130f80dea 100644
--- a/internal/jsre/deps/web3.js
+++ b/internal/jsre/deps/web3.js
@@ -13635,4 +13635,4 @@ if (typeof window !== 'undefined' && typeof window.Web3 === 'undefined') {
module.exports = Web3;
},{"./lib/web3":22}]},{},["web3"])
-//# sourceMappingURL=web3-light.js.map
+
diff --git a/internal/syncx/mutex.go b/internal/syncx/mutex.go
new file mode 100644
index 0000000000..96a21986c6
--- /dev/null
+++ b/internal/syncx/mutex.go
@@ -0,0 +1,64 @@
+// 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 syncx contains exotic synchronization primitives.
+package syncx
+
+// ClosableMutex is a mutex that can also be closed.
+// Once closed, it can never be taken again.
+type ClosableMutex struct {
+ ch chan struct{}
+}
+
+func NewClosableMutex() *ClosableMutex {
+ ch := make(chan struct{}, 1)
+ ch <- struct{}{}
+ return &ClosableMutex{ch}
+}
+
+// TryLock attempts to lock cm.
+// If the mutex is closed, TryLock returns false.
+func (cm *ClosableMutex) TryLock() bool {
+ _, ok := <-cm.ch
+ return ok
+}
+
+// MustLock locks cm.
+// If the mutex is closed, MustLock panics.
+func (cm *ClosableMutex) MustLock() {
+ _, ok := <-cm.ch
+ if !ok {
+ panic("mutex closed")
+ }
+}
+
+// Unlock unlocks cm.
+func (cm *ClosableMutex) Unlock() {
+ select {
+ case cm.ch <- struct{}{}:
+ default:
+ panic("Unlock of already-unlocked ClosableMutex")
+ }
+}
+
+// Close locks the mutex, then closes it.
+func (cm *ClosableMutex) Close() {
+ _, ok := <-cm.ch
+ if !ok {
+ panic("Close of already-closed ClosableMutex")
+ }
+ close(cm.ch)
+}
diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go
index 927dba1897..c4bdbaeb8d 100644
--- a/internal/web3ext/web3ext.go
+++ b/internal/web3ext/web3ext.go
@@ -223,6 +223,11 @@ web3._extend({
params: 1,
outputFormatter: console.log
}),
+ new web3._extend.Method({
+ name: 'getHeaderRlp',
+ call: 'debug_getHeaderRlp',
+ params: 1
+ }),
new web3._extend.Method({
name: 'getBlockRlp',
call: 'debug_getBlockRlp',
@@ -278,7 +283,8 @@ web3._extend({
new web3._extend.Method({
name: 'stacks',
call: 'debug_stacks',
- params: 0,
+ params: 1,
+ inputFormatter: [null],
outputFormatter: console.log
}),
new web3._extend.Method({
@@ -390,6 +396,12 @@ web3._extend({
params: 2,
inputFormatter: [null, null]
}),
+ new web3._extend.Method({
+ name: 'intermediateRoots',
+ call: 'debug_intermediateRoots',
+ params: 2,
+ inputFormatter: [null, null]
+ }),
new web3._extend.Method({
name: 'standardTraceBlockToFile',
call: 'debug_standardTraceBlockToFile',
@@ -453,6 +465,12 @@ web3._extend({
call: 'debug_freezeClient',
params: 1,
}),
+ new web3._extend.Method({
+ name: 'getAccessibleState',
+ call: 'debug_getAccessibleState',
+ params: 2,
+ inputFormatter:[web3._extend.formatters.inputBlockNumberFormatter, web3._extend.formatters.inputBlockNumberFormatter],
+ }),
],
properties: []
});
diff --git a/les/api_backend.go b/les/api_backend.go
index 9c80270da0..d5144dfbfb 100644
--- a/les/api_backend.go
+++ b/les/api_backend.go
@@ -20,7 +20,9 @@ import (
"context"
"errors"
"math/big"
+ "time"
+ "github.com/ethereum/go-ethereum"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/consensus"
@@ -30,7 +32,6 @@ import (
"github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/core/vm"
- "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/event"
@@ -257,8 +258,8 @@ func (b *LesApiBackend) SubscribeRemovedLogsEvent(ch chan<- core.RemovedLogsEven
return b.eth.blockchain.SubscribeRemovedLogsEvent(ch)
}
-func (b *LesApiBackend) Downloader() *downloader.Downloader {
- return b.eth.Downloader()
+func (b *LesApiBackend) SyncProgress() ethereum.SyncProgress {
+ return b.eth.Downloader().Progress()
}
func (b *LesApiBackend) ProtocolVersion() int {
@@ -293,6 +294,10 @@ func (b *LesApiBackend) RPCGasCap() uint64 {
return b.eth.config.RPCGasCap
}
+func (b *LesApiBackend) RPCEVMTimeout() time.Duration {
+ return b.eth.config.RPCEVMTimeout
+}
+
func (b *LesApiBackend) RPCTxFeeCap() float64 {
return b.eth.config.RPCTxFeeCap
}
diff --git a/les/api_test.go b/les/api_test.go
index f7017c5d98..6a19b0fe4f 100644
--- a/les/api_test.go
+++ b/les/api_test.go
@@ -32,8 +32,9 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/ethereum/go-ethereum/consensus/ethash"
"github.com/ethereum/go-ethereum/eth"
- "github.com/ethereum/go-ethereum/eth/downloader"
+ ethdownloader "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig"
+ "github.com/ethereum/go-ethereum/les/downloader"
"github.com/ethereum/go-ethereum/les/flowcontrol"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/node"
@@ -494,14 +495,14 @@ func testSim(t *testing.T, serverCount, clientCount int, serverDir, clientDir []
func newLesClientService(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) {
config := ethconfig.Defaults
- config.SyncMode = downloader.LightSync
+ config.SyncMode = (ethdownloader.SyncMode)(downloader.LightSync)
config.Ethash.PowMode = ethash.ModeFake
return New(stack, &config)
}
func newLesServerService(ctx *adapters.ServiceContext, stack *node.Node) (node.Lifecycle, error) {
config := ethconfig.Defaults
- config.SyncMode = downloader.FullSync
+ config.SyncMode = (ethdownloader.SyncMode)(downloader.FullSync)
config.LightServ = testServerCapacity
config.LightPeers = testMaxClients
ethereum, err := eth.New(stack, &config)
diff --git a/les/client.go b/les/client.go
index 1d8a2c6f9a..93319cb936 100644
--- a/les/client.go
+++ b/les/client.go
@@ -30,12 +30,12 @@ import (
"github.com/ethereum/go-ethereum/core/bloombits"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/ethconfig"
"github.com/ethereum/go-ethereum/eth/filters"
"github.com/ethereum/go-ethereum/eth/gasprice"
"github.com/ethereum/go-ethereum/event"
"github.com/ethereum/go-ethereum/internal/ethapi"
+ "github.com/ethereum/go-ethereum/les/downloader"
"github.com/ethereum/go-ethereum/les/vflux"
vfc "github.com/ethereum/go-ethereum/les/vflux/client"
"github.com/ethereum/go-ethereum/light"
@@ -88,7 +88,7 @@ func New(stack *node.Node, config *ethconfig.Config) (*LightEthereum, error) {
if err != nil {
return nil, err
}
- chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideLondon)
+ chainConfig, genesisHash, genesisErr := core.SetupGenesisBlockWithOverride(chainDb, config.Genesis, config.OverrideArrowGlacier)
if _, isCompat := genesisErr.(*params.ConfigCompatError); genesisErr != nil && !isCompat {
return nil, genesisErr
}
diff --git a/les/client_handler.go b/les/client_handler.go
index e95996c51f..9583bd57ca 100644
--- a/les/client_handler.go
+++ b/les/client_handler.go
@@ -28,8 +28,8 @@ import (
"github.com/ethereum/go-ethereum/common/mclock"
"github.com/ethereum/go-ethereum/core/forkid"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/eth/downloader"
"github.com/ethereum/go-ethereum/eth/protocols/eth"
+ "github.com/ethereum/go-ethereum/les/downloader"
"github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p"
@@ -100,11 +100,11 @@ func (h *clientHandler) runPeer(version uint, p *p2p.Peer, rw p2p.MsgReadWriter)
defer peer.close()
h.wg.Add(1)
defer h.wg.Done()
- err := h.handle(peer)
+ err := h.handle(peer, false)
return err
}
-func (h *clientHandler) handle(p *serverPeer) error {
+func (h *clientHandler) handle(p *serverPeer, noInitAnnounce bool) error {
if h.backend.peers.len() >= h.backend.config.LightPeers && !p.Peer.Info().Network.Trusted {
return p2p.DiscTooManyPeers
}
@@ -143,8 +143,11 @@ func (h *clientHandler) handle(p *serverPeer) error {
connectionTimer.Update(time.Duration(mclock.Now() - connectedAt))
serverConnectionGauge.Update(int64(h.backend.peers.len()))
}()
- h.fetcher.announce(p, &announceData{Hash: p.headInfo.Hash, Number: p.headInfo.Number, Td: p.headInfo.Td})
-
+ // It's mainly used in testing which requires discarding initial
+ // signal to prevent syncing.
+ if !noInitAnnounce {
+ h.fetcher.announce(p, &announceData{Hash: p.headInfo.Hash, Number: p.headInfo.Number, Td: p.headInfo.Td})
+ }
// Mark the peer starts to be served.
atomic.StoreUint32(&p.serving, 1)
defer atomic.StoreUint32(&p.serving, 0)
@@ -472,7 +475,7 @@ func (d *downloaderPeerNotify) registerPeer(p *serverPeer) {
handler: h,
peer: p,
}
- h.downloader.RegisterLightPeer(p.id, eth.ETH65, pc)
+ h.downloader.RegisterLightPeer(p.id, eth.ETH66, pc)
}
func (d *downloaderPeerNotify) unregisterPeer(p *serverPeer) {
diff --git a/les/downloader/api.go b/les/downloader/api.go
new file mode 100644
index 0000000000..2024d23dea
--- /dev/null
+++ b/les/downloader/api.go
@@ -0,0 +1,166 @@
+// 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 downloader
+
+import (
+ "context"
+ "sync"
+
+ "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/rpc"
+)
+
+// PublicDownloaderAPI provides an API which gives information about the current synchronisation status.
+// It offers only methods that operates on data that can be available to anyone without security risks.
+type PublicDownloaderAPI struct {
+ d *Downloader
+ mux *event.TypeMux
+ installSyncSubscription chan chan interface{}
+ uninstallSyncSubscription chan *uninstallSyncSubscriptionRequest
+}
+
+// NewPublicDownloaderAPI create a new PublicDownloaderAPI. The API has an internal event loop that
+// listens for events from the downloader through the global event mux. In case it receives one of
+// these events it broadcasts it to all syncing subscriptions that are installed through the
+// installSyncSubscription channel.
+func NewPublicDownloaderAPI(d *Downloader, m *event.TypeMux) *PublicDownloaderAPI {
+ api := &PublicDownloaderAPI{
+ d: d,
+ mux: m,
+ installSyncSubscription: make(chan chan interface{}),
+ uninstallSyncSubscription: make(chan *uninstallSyncSubscriptionRequest),
+ }
+
+ go api.eventLoop()
+
+ return api
+}
+
+// eventLoop runs a loop until the event mux closes. It will install and uninstall new
+// sync subscriptions and broadcasts sync status updates to the installed sync subscriptions.
+func (api *PublicDownloaderAPI) eventLoop() {
+ var (
+ sub = api.mux.Subscribe(StartEvent{}, DoneEvent{}, FailedEvent{})
+ syncSubscriptions = make(map[chan interface{}]struct{})
+ )
+
+ for {
+ select {
+ case i := <-api.installSyncSubscription:
+ syncSubscriptions[i] = struct{}{}
+ case u := <-api.uninstallSyncSubscription:
+ delete(syncSubscriptions, u.c)
+ close(u.uninstalled)
+ case event := <-sub.Chan():
+ if event == nil {
+ return
+ }
+
+ var notification interface{}
+ switch event.Data.(type) {
+ case StartEvent:
+ notification = &SyncingResult{
+ Syncing: true,
+ Status: api.d.Progress(),
+ }
+ case DoneEvent, FailedEvent:
+ notification = false
+ }
+ // broadcast
+ for c := range syncSubscriptions {
+ c <- notification
+ }
+ }
+ }
+}
+
+// Syncing provides information when this nodes starts synchronising with the Ethereum network and when it's finished.
+func (api *PublicDownloaderAPI) Syncing(ctx context.Context) (*rpc.Subscription, error) {
+ notifier, supported := rpc.NotifierFromContext(ctx)
+ if !supported {
+ return &rpc.Subscription{}, rpc.ErrNotificationsUnsupported
+ }
+
+ rpcSub := notifier.CreateSubscription()
+
+ go func() {
+ statuses := make(chan interface{})
+ sub := api.SubscribeSyncStatus(statuses)
+
+ for {
+ select {
+ case status := <-statuses:
+ notifier.Notify(rpcSub.ID, status)
+ case <-rpcSub.Err():
+ sub.Unsubscribe()
+ return
+ case <-notifier.Closed():
+ sub.Unsubscribe()
+ return
+ }
+ }
+ }()
+
+ return rpcSub, nil
+}
+
+// SyncingResult provides information about the current synchronisation status for this node.
+type SyncingResult struct {
+ Syncing bool `json:"syncing"`
+ Status ethereum.SyncProgress `json:"status"`
+}
+
+// uninstallSyncSubscriptionRequest uninstalles a syncing subscription in the API event loop.
+type uninstallSyncSubscriptionRequest struct {
+ c chan interface{}
+ uninstalled chan interface{}
+}
+
+// SyncStatusSubscription represents a syncing subscription.
+type SyncStatusSubscription struct {
+ api *PublicDownloaderAPI // register subscription in event loop of this api instance
+ c chan interface{} // channel where events are broadcasted to
+ unsubOnce sync.Once // make sure unsubscribe logic is executed once
+}
+
+// Unsubscribe uninstalls the subscription from the DownloadAPI event loop.
+// The status channel that was passed to subscribeSyncStatus isn't used anymore
+// after this method returns.
+func (s *SyncStatusSubscription) Unsubscribe() {
+ s.unsubOnce.Do(func() {
+ req := uninstallSyncSubscriptionRequest{s.c, make(chan interface{})}
+ s.api.uninstallSyncSubscription <- &req
+
+ for {
+ select {
+ case <-s.c:
+ // drop new status events until uninstall confirmation
+ continue
+ case <-req.uninstalled:
+ return
+ }
+ }
+ })
+}
+
+// SubscribeSyncStatus creates a subscription that will broadcast new synchronisation updates.
+// The given channel must receive interface values, the result can either
+func (api *PublicDownloaderAPI) SubscribeSyncStatus(status chan interface{}) *SyncStatusSubscription {
+ api.installSyncSubscription <- status
+ return &SyncStatusSubscription{api: api, c: status}
+}
diff --git a/les/downloader/downloader.go b/les/downloader/downloader.go
new file mode 100644
index 0000000000..e7dfc4158e
--- /dev/null
+++ b/les/downloader/downloader.go
@@ -0,0 +1,2014 @@
+// 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 .
+
+// This is a temporary package whilst working on the eth/66 blocking refactors.
+// After that work is done, les needs to be refactored to use the new package,
+// or alternatively use a stripped down version of it. Either way, we need to
+// keep the changes scoped so duplicating temporarily seems the sanest.
+package downloader
+
+import (
+ "errors"
+ "fmt"
+ "math/big"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/state/snapshot"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/eth/protocols/eth"
+ "github.com/ethereum/go-ethereum/eth/protocols/snap"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/metrics"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/trie"
+)
+
+var (
+ MaxBlockFetch = 128 // Amount of blocks to be fetched per retrieval request
+ MaxHeaderFetch = 192 // Amount of block headers to be fetched per retrieval request
+ MaxSkeletonSize = 128 // Number of header fetches to need for a skeleton assembly
+ MaxReceiptFetch = 256 // Amount of transaction receipts to allow fetching per request
+ MaxStateFetch = 384 // Amount of node state values to allow fetching per request
+
+ maxQueuedHeaders = 32 * 1024 // [eth/62] Maximum number of headers to queue for import (DOS protection)
+ maxHeadersProcess = 2048 // Number of header download results to import at once into the chain
+ maxResultsProcess = 2048 // Number of content download results to import at once into the chain
+ fullMaxForkAncestry uint64 = params.FullImmutabilityThreshold // Maximum chain reorganisation (locally redeclared so tests can reduce it)
+ lightMaxForkAncestry uint64 = params.LightImmutabilityThreshold // Maximum chain reorganisation (locally redeclared so tests can reduce it)
+
+ reorgProtThreshold = 48 // Threshold number of recent blocks to disable mini reorg protection
+ reorgProtHeaderDelay = 2 // Number of headers to delay delivering to cover mini reorgs
+
+ fsHeaderCheckFrequency = 100 // Verification frequency of the downloaded headers during fast sync
+ fsHeaderSafetyNet = 2048 // Number of headers to discard in case a chain violation is detected
+ fsHeaderForceVerify = 24 // Number of headers to verify before and after the pivot to accept it
+ fsHeaderContCheck = 3 * time.Second // Time interval to check for header continuations during state download
+ fsMinFullBlocks = 64 // Number of blocks to retrieve fully even in fast sync
+)
+
+var (
+ errBusy = errors.New("busy")
+ errUnknownPeer = errors.New("peer is unknown or unhealthy")
+ errBadPeer = errors.New("action from bad peer ignored")
+ errStallingPeer = errors.New("peer is stalling")
+ errUnsyncedPeer = errors.New("unsynced peer")
+ errNoPeers = errors.New("no peers to keep download active")
+ errTimeout = errors.New("timeout")
+ errEmptyHeaderSet = errors.New("empty header set by peer")
+ errPeersUnavailable = errors.New("no peers available or all tried for download")
+ errInvalidAncestor = errors.New("retrieved ancestor is invalid")
+ errInvalidChain = errors.New("retrieved hash chain is invalid")
+ errInvalidBody = errors.New("retrieved block body is invalid")
+ errInvalidReceipt = errors.New("retrieved receipt is invalid")
+ errCancelStateFetch = errors.New("state data download canceled (requested)")
+ errCancelContentProcessing = errors.New("content processing canceled (requested)")
+ errCanceled = errors.New("syncing canceled (requested)")
+ errNoSyncActive = errors.New("no sync active")
+ errTooOld = errors.New("peer's protocol version too old")
+ errNoAncestorFound = errors.New("no common ancestor found")
+)
+
+type Downloader struct {
+ mode uint32 // Synchronisation mode defining the strategy used (per sync cycle), use d.getMode() to get the SyncMode
+ mux *event.TypeMux // Event multiplexer to announce sync operation events
+
+ checkpoint uint64 // Checkpoint block number to enforce head against (e.g. fast sync)
+ genesis uint64 // Genesis block number to limit sync to (e.g. light client CHT)
+ queue *queue // Scheduler for selecting the hashes to download
+ peers *peerSet // Set of active peers from which download can proceed
+
+ stateDB ethdb.Database // Database to state sync into (and deduplicate via)
+ stateBloom *trie.SyncBloom // Bloom filter for fast trie node and contract code existence checks
+
+ // Statistics
+ syncStatsChainOrigin uint64 // Origin block number where syncing started at
+ syncStatsChainHeight uint64 // Highest block number known when syncing started
+ syncStatsState stateSyncStats
+ syncStatsLock sync.RWMutex // Lock protecting the sync stats fields
+
+ lightchain LightChain
+ blockchain BlockChain
+
+ // Callbacks
+ dropPeer peerDropFn // Drops a peer for misbehaving
+
+ // Status
+ synchroniseMock func(id string, hash common.Hash) error // Replacement for synchronise during testing
+ synchronising int32
+ notified int32
+ committed int32
+ ancientLimit uint64 // The maximum block number which can be regarded as ancient data.
+
+ // Channels
+ headerCh chan dataPack // Channel receiving inbound block headers
+ bodyCh chan dataPack // Channel receiving inbound block bodies
+ receiptCh chan dataPack // Channel receiving inbound receipts
+ bodyWakeCh chan bool // Channel to signal the block body fetcher of new tasks
+ receiptWakeCh chan bool // Channel to signal the receipt fetcher of new tasks
+ headerProcCh chan []*types.Header // Channel to feed the header processor new tasks
+
+ // State sync
+ pivotHeader *types.Header // Pivot block header to dynamically push the syncing state root
+ pivotLock sync.RWMutex // Lock protecting pivot header reads from updates
+
+ snapSync bool // Whether to run state sync over the snap protocol
+ SnapSyncer *snap.Syncer // TODO(karalabe): make private! hack for now
+ stateSyncStart chan *stateSync
+ trackStateReq chan *stateReq
+ stateCh chan dataPack // Channel receiving inbound node state data
+
+ // Cancellation and termination
+ cancelPeer string // Identifier of the peer currently being used as the master (cancel on drop)
+ cancelCh chan struct{} // Channel to cancel mid-flight syncs
+ cancelLock sync.RWMutex // Lock to protect the cancel channel and peer in delivers
+ cancelWg sync.WaitGroup // Make sure all fetcher goroutines have exited.
+
+ quitCh chan struct{} // Quit channel to signal termination
+ quitLock sync.Mutex // Lock to prevent double closes
+
+ // Testing hooks
+ syncInitHook func(uint64, uint64) // Method to call upon initiating a new sync run
+ bodyFetchHook func([]*types.Header) // Method to call upon starting a block body fetch
+ receiptFetchHook func([]*types.Header) // Method to call upon starting a receipt fetch
+ chainInsertHook func([]*fetchResult) // Method to call upon inserting a chain of blocks (possibly in multiple invocations)
+}
+
+// LightChain encapsulates functions required to synchronise a light chain.
+type LightChain interface {
+ // HasHeader verifies a header's presence in the local chain.
+ HasHeader(common.Hash, uint64) bool
+
+ // GetHeaderByHash retrieves a header from the local chain.
+ GetHeaderByHash(common.Hash) *types.Header
+
+ // CurrentHeader retrieves the head header from the local chain.
+ CurrentHeader() *types.Header
+
+ // GetTd returns the total difficulty of a local block.
+ GetTd(common.Hash, uint64) *big.Int
+
+ // InsertHeaderChain inserts a batch of headers into the local chain.
+ InsertHeaderChain([]*types.Header, int) (int, error)
+
+ // SetHead rewinds the local chain to a new head.
+ SetHead(uint64) error
+}
+
+// BlockChain encapsulates functions required to sync a (full or fast) blockchain.
+type BlockChain interface {
+ LightChain
+
+ // HasBlock verifies a block's presence in the local chain.
+ HasBlock(common.Hash, uint64) bool
+
+ // HasFastBlock verifies a fast block's presence in the local chain.
+ HasFastBlock(common.Hash, uint64) bool
+
+ // GetBlockByHash retrieves a block from the local chain.
+ GetBlockByHash(common.Hash) *types.Block
+
+ // CurrentBlock retrieves the head block from the local chain.
+ CurrentBlock() *types.Block
+
+ // CurrentFastBlock retrieves the head fast block from the local chain.
+ CurrentFastBlock() *types.Block
+
+ // FastSyncCommitHead directly commits the head block to a certain entity.
+ FastSyncCommitHead(common.Hash) error
+
+ // InsertChain inserts a batch of blocks into the local chain.
+ InsertChain(types.Blocks) (int, error)
+
+ // InsertReceiptChain inserts a batch of receipts into the local chain.
+ InsertReceiptChain(types.Blocks, []types.Receipts, uint64) (int, error)
+
+ // Snapshots returns the blockchain snapshot tree to paused it during sync.
+ Snapshots() *snapshot.Tree
+}
+
+// New creates a new downloader to fetch hashes and blocks from remote peers.
+func New(checkpoint uint64, stateDb ethdb.Database, stateBloom *trie.SyncBloom, mux *event.TypeMux, chain BlockChain, lightchain LightChain, dropPeer peerDropFn) *Downloader {
+ if lightchain == nil {
+ lightchain = chain
+ }
+ dl := &Downloader{
+ stateDB: stateDb,
+ stateBloom: stateBloom,
+ mux: mux,
+ checkpoint: checkpoint,
+ queue: newQueue(blockCacheMaxItems, blockCacheInitialItems),
+ peers: newPeerSet(),
+ blockchain: chain,
+ lightchain: lightchain,
+ dropPeer: dropPeer,
+ headerCh: make(chan dataPack, 1),
+ bodyCh: make(chan dataPack, 1),
+ receiptCh: make(chan dataPack, 1),
+ bodyWakeCh: make(chan bool, 1),
+ receiptWakeCh: make(chan bool, 1),
+ headerProcCh: make(chan []*types.Header, 1),
+ quitCh: make(chan struct{}),
+ stateCh: make(chan dataPack),
+ SnapSyncer: snap.NewSyncer(stateDb),
+ stateSyncStart: make(chan *stateSync),
+ syncStatsState: stateSyncStats{
+ processed: rawdb.ReadFastTrieProgress(stateDb),
+ },
+ trackStateReq: make(chan *stateReq),
+ }
+ go dl.stateFetcher()
+ return dl
+}
+
+// Progress retrieves the synchronisation boundaries, specifically the origin
+// block where synchronisation started at (may have failed/suspended); the block
+// or header sync is currently at; and the latest known block which the sync targets.
+//
+// In addition, during the state download phase of fast synchronisation the number
+// of processed and the total number of known states are also returned. Otherwise
+// these are zero.
+func (d *Downloader) Progress() ethereum.SyncProgress {
+ // Lock the current stats and return the progress
+ d.syncStatsLock.RLock()
+ defer d.syncStatsLock.RUnlock()
+
+ current := uint64(0)
+ mode := d.getMode()
+ switch {
+ case d.blockchain != nil && mode == FullSync:
+ current = d.blockchain.CurrentBlock().NumberU64()
+ case d.blockchain != nil && mode == FastSync:
+ current = d.blockchain.CurrentFastBlock().NumberU64()
+ case d.lightchain != nil:
+ current = d.lightchain.CurrentHeader().Number.Uint64()
+ default:
+ log.Error("Unknown downloader chain/mode combo", "light", d.lightchain != nil, "full", d.blockchain != nil, "mode", mode)
+ }
+ return ethereum.SyncProgress{
+ StartingBlock: d.syncStatsChainOrigin,
+ CurrentBlock: current,
+ HighestBlock: d.syncStatsChainHeight,
+ PulledStates: d.syncStatsState.processed,
+ KnownStates: d.syncStatsState.processed + d.syncStatsState.pending,
+ }
+}
+
+// Synchronising returns whether the downloader is currently retrieving blocks.
+func (d *Downloader) Synchronising() bool {
+ return atomic.LoadInt32(&d.synchronising) > 0
+}
+
+// RegisterPeer injects a new download peer into the set of block source to be
+// used for fetching hashes and blocks from.
+func (d *Downloader) RegisterPeer(id string, version uint, peer Peer) error {
+ var logger log.Logger
+ if len(id) < 16 {
+ // Tests use short IDs, don't choke on them
+ logger = log.New("peer", id)
+ } else {
+ logger = log.New("peer", id[:8])
+ }
+ logger.Trace("Registering sync peer")
+ if err := d.peers.Register(newPeerConnection(id, version, peer, logger)); err != nil {
+ logger.Error("Failed to register sync peer", "err", err)
+ return err
+ }
+ return nil
+}
+
+// RegisterLightPeer injects a light client peer, wrapping it so it appears as a regular peer.
+func (d *Downloader) RegisterLightPeer(id string, version uint, peer LightPeer) error {
+ return d.RegisterPeer(id, version, &lightPeerWrapper{peer})
+}
+
+// UnregisterPeer remove a peer from the known list, preventing any action from
+// the specified peer. An effort is also made to return any pending fetches into
+// the queue.
+func (d *Downloader) UnregisterPeer(id string) error {
+ // Unregister the peer from the active peer set and revoke any fetch tasks
+ var logger log.Logger
+ if len(id) < 16 {
+ // Tests use short IDs, don't choke on them
+ logger = log.New("peer", id)
+ } else {
+ logger = log.New("peer", id[:8])
+ }
+ logger.Trace("Unregistering sync peer")
+ if err := d.peers.Unregister(id); err != nil {
+ logger.Error("Failed to unregister sync peer", "err", err)
+ return err
+ }
+ d.queue.Revoke(id)
+
+ return nil
+}
+
+// Synchronise tries to sync up our local block chain with a remote peer, both
+// adding various sanity checks as well as wrapping it with various log entries.
+func (d *Downloader) Synchronise(id string, head common.Hash, td *big.Int, mode SyncMode) error {
+ err := d.synchronise(id, head, td, mode)
+
+ switch err {
+ case nil, errBusy, errCanceled:
+ return err
+ }
+ if errors.Is(err, errInvalidChain) || errors.Is(err, errBadPeer) || errors.Is(err, errTimeout) ||
+ errors.Is(err, errStallingPeer) || errors.Is(err, errUnsyncedPeer) || errors.Is(err, errEmptyHeaderSet) ||
+ errors.Is(err, errPeersUnavailable) || errors.Is(err, errTooOld) || errors.Is(err, errInvalidAncestor) {
+ log.Warn("Synchronisation failed, dropping peer", "peer", id, "err", err)
+ if d.dropPeer == nil {
+ // The dropPeer method is nil when `--copydb` is used for a local copy.
+ // Timeouts can occur if e.g. compaction hits at the wrong time, and can be ignored
+ log.Warn("Downloader wants to drop peer, but peerdrop-function is not set", "peer", id)
+ } else {
+ d.dropPeer(id)
+ }
+ return err
+ }
+ log.Warn("Synchronisation failed, retrying", "err", err)
+ return err
+}
+
+// synchronise will select the peer and use it for synchronising. If an empty string is given
+// it will use the best peer possible and synchronize if its TD is higher than our own. If any of the
+// checks fail an error will be returned. This method is synchronous
+func (d *Downloader) synchronise(id string, hash common.Hash, td *big.Int, mode SyncMode) error {
+ // Mock out the synchronisation if testing
+ if d.synchroniseMock != nil {
+ return d.synchroniseMock(id, hash)
+ }
+ // Make sure only one goroutine is ever allowed past this point at once
+ if !atomic.CompareAndSwapInt32(&d.synchronising, 0, 1) {
+ return errBusy
+ }
+ defer atomic.StoreInt32(&d.synchronising, 0)
+
+ // Post a user notification of the sync (only once per session)
+ if atomic.CompareAndSwapInt32(&d.notified, 0, 1) {
+ log.Info("Block synchronisation started")
+ }
+ // If we are already full syncing, but have a fast-sync bloom filter laying
+ // around, make sure it doesn't use memory any more. This is a special case
+ // when the user attempts to fast sync a new empty network.
+ if mode == FullSync && d.stateBloom != nil {
+ d.stateBloom.Close()
+ }
+ // If snap sync was requested, create the snap scheduler and switch to fast
+ // sync mode. Long term we could drop fast sync or merge the two together,
+ // but until snap becomes prevalent, we should support both. TODO(karalabe).
+ if mode == SnapSync {
+ if !d.snapSync {
+ // Snap sync uses the snapshot namespace to store potentially flakey data until
+ // sync completely heals and finishes. Pause snapshot maintenance in the mean
+ // time to prevent access.
+ if snapshots := d.blockchain.Snapshots(); snapshots != nil { // Only nil in tests
+ snapshots.Disable()
+ }
+ log.Warn("Enabling snapshot sync prototype")
+ d.snapSync = true
+ }
+ mode = FastSync
+ }
+ // Reset the queue, peer set and wake channels to clean any internal leftover state
+ d.queue.Reset(blockCacheMaxItems, blockCacheInitialItems)
+ d.peers.Reset()
+
+ for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh} {
+ select {
+ case <-ch:
+ default:
+ }
+ }
+ for _, ch := range []chan dataPack{d.headerCh, d.bodyCh, d.receiptCh} {
+ for empty := false; !empty; {
+ select {
+ case <-ch:
+ default:
+ empty = true
+ }
+ }
+ }
+ for empty := false; !empty; {
+ select {
+ case <-d.headerProcCh:
+ default:
+ empty = true
+ }
+ }
+ // Create cancel channel for aborting mid-flight and mark the master peer
+ d.cancelLock.Lock()
+ d.cancelCh = make(chan struct{})
+ d.cancelPeer = id
+ d.cancelLock.Unlock()
+
+ defer d.Cancel() // No matter what, we can't leave the cancel channel open
+
+ // Atomically set the requested sync mode
+ atomic.StoreUint32(&d.mode, uint32(mode))
+
+ // Retrieve the origin peer and initiate the downloading process
+ p := d.peers.Peer(id)
+ if p == nil {
+ return errUnknownPeer
+ }
+ return d.syncWithPeer(p, hash, td)
+}
+
+func (d *Downloader) getMode() SyncMode {
+ return SyncMode(atomic.LoadUint32(&d.mode))
+}
+
+// syncWithPeer starts a block synchronization based on the hash chain from the
+// specified peer and head hash.
+func (d *Downloader) syncWithPeer(p *peerConnection, hash common.Hash, td *big.Int) (err error) {
+ d.mux.Post(StartEvent{})
+ defer func() {
+ // reset on error
+ if err != nil {
+ d.mux.Post(FailedEvent{err})
+ } else {
+ latest := d.lightchain.CurrentHeader()
+ d.mux.Post(DoneEvent{latest})
+ }
+ }()
+ if p.version < eth.ETH66 {
+ return fmt.Errorf("%w: advertized %d < required %d", errTooOld, p.version, eth.ETH66)
+ }
+ mode := d.getMode()
+
+ log.Debug("Synchronising with the network", "peer", p.id, "eth", p.version, "head", hash, "td", td, "mode", mode)
+ defer func(start time.Time) {
+ log.Debug("Synchronisation terminated", "elapsed", common.PrettyDuration(time.Since(start)))
+ }(time.Now())
+
+ // Look up the sync boundaries: the common ancestor and the target block
+ latest, pivot, err := d.fetchHead(p)
+ if err != nil {
+ return err
+ }
+ if mode == FastSync && pivot == nil {
+ // If no pivot block was returned, the head is below the min full block
+ // threshold (i.e. new chain). In that case we won't really fast sync
+ // anyway, but still need a valid pivot block to avoid some code hitting
+ // nil panics on an access.
+ pivot = d.blockchain.CurrentBlock().Header()
+ }
+ height := latest.Number.Uint64()
+
+ origin, err := d.findAncestor(p, latest)
+ if err != nil {
+ return err
+ }
+ d.syncStatsLock.Lock()
+ if d.syncStatsChainHeight <= origin || d.syncStatsChainOrigin > origin {
+ d.syncStatsChainOrigin = origin
+ }
+ d.syncStatsChainHeight = height
+ d.syncStatsLock.Unlock()
+
+ // Ensure our origin point is below any fast sync pivot point
+ if mode == FastSync {
+ if height <= uint64(fsMinFullBlocks) {
+ origin = 0
+ } else {
+ pivotNumber := pivot.Number.Uint64()
+ if pivotNumber <= origin {
+ origin = pivotNumber - 1
+ }
+ // Write out the pivot into the database so a rollback beyond it will
+ // reenable fast sync
+ rawdb.WriteLastPivotNumber(d.stateDB, pivotNumber)
+ }
+ }
+ d.committed = 1
+ if mode == FastSync && pivot.Number.Uint64() != 0 {
+ d.committed = 0
+ }
+ if mode == FastSync {
+ // Set the ancient data limitation.
+ // If we are running fast sync, all block data older than ancientLimit will be
+ // written to the ancient store. More recent data will be written to the active
+ // database and will wait for the freezer to migrate.
+ //
+ // If there is a checkpoint available, then calculate the ancientLimit through
+ // that. Otherwise calculate the ancient limit through the advertised height
+ // of the remote peer.
+ //
+ // The reason for picking checkpoint first is that a malicious peer can give us
+ // a fake (very high) height, forcing the ancient limit to also be very high.
+ // The peer would start to feed us valid blocks until head, resulting in all of
+ // the blocks might be written into the ancient store. A following mini-reorg
+ // could cause issues.
+ if d.checkpoint != 0 && d.checkpoint > fullMaxForkAncestry+1 {
+ d.ancientLimit = d.checkpoint
+ } else if height > fullMaxForkAncestry+1 {
+ d.ancientLimit = height - fullMaxForkAncestry - 1
+ } else {
+ d.ancientLimit = 0
+ }
+ frozen, _ := d.stateDB.Ancients() // Ignore the error here since light client can also hit here.
+
+ // If a part of blockchain data has already been written into active store,
+ // disable the ancient style insertion explicitly.
+ if origin >= frozen && frozen != 0 {
+ d.ancientLimit = 0
+ log.Info("Disabling direct-ancient mode", "origin", origin, "ancient", frozen-1)
+ } else if d.ancientLimit > 0 {
+ log.Debug("Enabling direct-ancient mode", "ancient", d.ancientLimit)
+ }
+ // Rewind the ancient store and blockchain if reorg happens.
+ if origin+1 < frozen {
+ if err := d.lightchain.SetHead(origin + 1); err != nil {
+ return err
+ }
+ }
+ }
+ // Initiate the sync using a concurrent header and content retrieval algorithm
+ d.queue.Prepare(origin+1, mode)
+ if d.syncInitHook != nil {
+ d.syncInitHook(origin, height)
+ }
+ fetchers := []func() error{
+ func() error { return d.fetchHeaders(p, origin+1) }, // Headers are always retrieved
+ func() error { return d.fetchBodies(origin + 1) }, // Bodies are retrieved during normal and fast sync
+ func() error { return d.fetchReceipts(origin + 1) }, // Receipts are retrieved during fast sync
+ func() error { return d.processHeaders(origin+1, td) },
+ }
+ if mode == FastSync {
+ d.pivotLock.Lock()
+ d.pivotHeader = pivot
+ d.pivotLock.Unlock()
+
+ fetchers = append(fetchers, func() error { return d.processFastSyncContent() })
+ } else if mode == FullSync {
+ fetchers = append(fetchers, d.processFullSyncContent)
+ }
+ return d.spawnSync(fetchers)
+}
+
+// spawnSync runs d.process and all given fetcher functions to completion in
+// separate goroutines, returning the first error that appears.
+func (d *Downloader) spawnSync(fetchers []func() error) error {
+ errc := make(chan error, len(fetchers))
+ d.cancelWg.Add(len(fetchers))
+ for _, fn := range fetchers {
+ fn := fn
+ go func() { defer d.cancelWg.Done(); errc <- fn() }()
+ }
+ // Wait for the first error, then terminate the others.
+ var err error
+ for i := 0; i < len(fetchers); i++ {
+ if i == len(fetchers)-1 {
+ // Close the queue when all fetchers have exited.
+ // This will cause the block processor to end when
+ // it has processed the queue.
+ d.queue.Close()
+ }
+ if err = <-errc; err != nil && err != errCanceled {
+ break
+ }
+ }
+ d.queue.Close()
+ d.Cancel()
+ return err
+}
+
+// cancel aborts all of the operations and resets the queue. However, cancel does
+// not wait for the running download goroutines to finish. This method should be
+// used when cancelling the downloads from inside the downloader.
+func (d *Downloader) cancel() {
+ // Close the current cancel channel
+ d.cancelLock.Lock()
+ defer d.cancelLock.Unlock()
+
+ if d.cancelCh != nil {
+ select {
+ case <-d.cancelCh:
+ // Channel was already closed
+ default:
+ close(d.cancelCh)
+ }
+ }
+}
+
+// Cancel aborts all of the operations and waits for all download goroutines to
+// finish before returning.
+func (d *Downloader) Cancel() {
+ d.cancel()
+ d.cancelWg.Wait()
+}
+
+// Terminate interrupts the downloader, canceling all pending operations.
+// The downloader cannot be reused after calling Terminate.
+func (d *Downloader) Terminate() {
+ // Close the termination channel (make sure double close is allowed)
+ d.quitLock.Lock()
+ select {
+ case <-d.quitCh:
+ default:
+ close(d.quitCh)
+ }
+ if d.stateBloom != nil {
+ d.stateBloom.Close()
+ }
+ d.quitLock.Unlock()
+
+ // Cancel any pending download requests
+ d.Cancel()
+}
+
+// fetchHead retrieves the head header and prior pivot block (if available) from
+// a remote peer.
+func (d *Downloader) fetchHead(p *peerConnection) (head *types.Header, pivot *types.Header, err error) {
+ p.log.Debug("Retrieving remote chain head")
+ mode := d.getMode()
+
+ // Request the advertised remote head block and wait for the response
+ latest, _ := p.peer.Head()
+ fetch := 1
+ if mode == FastSync {
+ fetch = 2 // head + pivot headers
+ }
+ go p.peer.RequestHeadersByHash(latest, fetch, fsMinFullBlocks-1, true)
+
+ ttl := d.peers.rates.TargetTimeout()
+ timeout := time.After(ttl)
+ for {
+ select {
+ case <-d.cancelCh:
+ return nil, nil, errCanceled
+
+ case packet := <-d.headerCh:
+ // Discard anything not from the origin peer
+ if packet.PeerId() != p.id {
+ log.Debug("Received headers from incorrect peer", "peer", packet.PeerId())
+ break
+ }
+ // Make sure the peer gave us at least one and at most the requested headers
+ headers := packet.(*headerPack).headers
+ if len(headers) == 0 || len(headers) > fetch {
+ return nil, nil, fmt.Errorf("%w: returned headers %d != requested %d", errBadPeer, len(headers), fetch)
+ }
+ // The first header needs to be the head, validate against the checkpoint
+ // and request. If only 1 header was returned, make sure there's no pivot
+ // or there was not one requested.
+ head := headers[0]
+ if (mode == FastSync || mode == LightSync) && head.Number.Uint64() < d.checkpoint {
+ return nil, nil, fmt.Errorf("%w: remote head %d below checkpoint %d", errUnsyncedPeer, head.Number, d.checkpoint)
+ }
+ if len(headers) == 1 {
+ if mode == FastSync && head.Number.Uint64() > uint64(fsMinFullBlocks) {
+ return nil, nil, fmt.Errorf("%w: no pivot included along head header", errBadPeer)
+ }
+ p.log.Debug("Remote head identified, no pivot", "number", head.Number, "hash", head.Hash())
+ return head, nil, nil
+ }
+ // At this point we have 2 headers in total and the first is the
+ // validated head of the chain. Check the pivot number and return,
+ pivot := headers[1]
+ if pivot.Number.Uint64() != head.Number.Uint64()-uint64(fsMinFullBlocks) {
+ return nil, nil, fmt.Errorf("%w: remote pivot %d != requested %d", errInvalidChain, pivot.Number, head.Number.Uint64()-uint64(fsMinFullBlocks))
+ }
+ return head, pivot, nil
+
+ case <-timeout:
+ p.log.Debug("Waiting for head header timed out", "elapsed", ttl)
+ return nil, nil, errTimeout
+
+ case <-d.bodyCh:
+ case <-d.receiptCh:
+ // Out of bounds delivery, ignore
+ }
+ }
+}
+
+// calculateRequestSpan calculates what headers to request from a peer when trying to determine the
+// common ancestor.
+// It returns parameters to be used for peer.RequestHeadersByNumber:
+// from - starting block number
+// count - number of headers to request
+// skip - number of headers to skip
+// and also returns 'max', the last block which is expected to be returned by the remote peers,
+// given the (from,count,skip)
+func calculateRequestSpan(remoteHeight, localHeight uint64) (int64, int, int, uint64) {
+ var (
+ from int
+ count int
+ MaxCount = MaxHeaderFetch / 16
+ )
+ // requestHead is the highest block that we will ask for. If requestHead is not offset,
+ // the highest block that we will get is 16 blocks back from head, which means we
+ // will fetch 14 or 15 blocks unnecessarily in the case the height difference
+ // between us and the peer is 1-2 blocks, which is most common
+ requestHead := int(remoteHeight) - 1
+ if requestHead < 0 {
+ requestHead = 0
+ }
+ // requestBottom is the lowest block we want included in the query
+ // Ideally, we want to include the one just below our own head
+ requestBottom := int(localHeight - 1)
+ if requestBottom < 0 {
+ requestBottom = 0
+ }
+ totalSpan := requestHead - requestBottom
+ span := 1 + totalSpan/MaxCount
+ if span < 2 {
+ span = 2
+ }
+ if span > 16 {
+ span = 16
+ }
+
+ count = 1 + totalSpan/span
+ if count > MaxCount {
+ count = MaxCount
+ }
+ if count < 2 {
+ count = 2
+ }
+ from = requestHead - (count-1)*span
+ if from < 0 {
+ from = 0
+ }
+ max := from + (count-1)*span
+ return int64(from), count, span - 1, uint64(max)
+}
+
+// findAncestor tries to locate the common ancestor link of the local chain and
+// a remote peers blockchain. In the general case when our node was in sync and
+// on the correct chain, checking the top N links should already get us a match.
+// In the rare scenario when we ended up on a long reorganisation (i.e. none of
+// the head links match), we do a binary search to find the common ancestor.
+func (d *Downloader) findAncestor(p *peerConnection, remoteHeader *types.Header) (uint64, error) {
+ // Figure out the valid ancestor range to prevent rewrite attacks
+ var (
+ floor = int64(-1)
+ localHeight uint64
+ remoteHeight = remoteHeader.Number.Uint64()
+ )
+ mode := d.getMode()
+ switch mode {
+ case FullSync:
+ localHeight = d.blockchain.CurrentBlock().NumberU64()
+ case FastSync:
+ localHeight = d.blockchain.CurrentFastBlock().NumberU64()
+ default:
+ localHeight = d.lightchain.CurrentHeader().Number.Uint64()
+ }
+ p.log.Debug("Looking for common ancestor", "local", localHeight, "remote", remoteHeight)
+
+ // Recap floor value for binary search
+ maxForkAncestry := fullMaxForkAncestry
+ if d.getMode() == LightSync {
+ maxForkAncestry = lightMaxForkAncestry
+ }
+ if localHeight >= maxForkAncestry {
+ // We're above the max reorg threshold, find the earliest fork point
+ floor = int64(localHeight - maxForkAncestry)
+ }
+ // If we're doing a light sync, ensure the floor doesn't go below the CHT, as
+ // all headers before that point will be missing.
+ if mode == LightSync {
+ // If we don't know the current CHT position, find it
+ if d.genesis == 0 {
+ header := d.lightchain.CurrentHeader()
+ for header != nil {
+ d.genesis = header.Number.Uint64()
+ if floor >= int64(d.genesis)-1 {
+ break
+ }
+ header = d.lightchain.GetHeaderByHash(header.ParentHash)
+ }
+ }
+ // We already know the "genesis" block number, cap floor to that
+ if floor < int64(d.genesis)-1 {
+ floor = int64(d.genesis) - 1
+ }
+ }
+
+ ancestor, err := d.findAncestorSpanSearch(p, mode, remoteHeight, localHeight, floor)
+ if err == nil {
+ return ancestor, nil
+ }
+ // The returned error was not nil.
+ // If the error returned does not reflect that a common ancestor was not found, return it.
+ // If the error reflects that a common ancestor was not found, continue to binary search,
+ // where the error value will be reassigned.
+ if !errors.Is(err, errNoAncestorFound) {
+ return 0, err
+ }
+
+ ancestor, err = d.findAncestorBinarySearch(p, mode, remoteHeight, floor)
+ if err != nil {
+ return 0, err
+ }
+ return ancestor, nil
+}
+
+func (d *Downloader) findAncestorSpanSearch(p *peerConnection, mode SyncMode, remoteHeight, localHeight uint64, floor int64) (commonAncestor uint64, err error) {
+ from, count, skip, max := calculateRequestSpan(remoteHeight, localHeight)
+
+ p.log.Trace("Span searching for common ancestor", "count", count, "from", from, "skip", skip)
+ go p.peer.RequestHeadersByNumber(uint64(from), count, skip, false)
+
+ // Wait for the remote response to the head fetch
+ number, hash := uint64(0), common.Hash{}
+
+ ttl := d.peers.rates.TargetTimeout()
+ timeout := time.After(ttl)
+
+ for finished := false; !finished; {
+ select {
+ case <-d.cancelCh:
+ return 0, errCanceled
+
+ case packet := <-d.headerCh:
+ // Discard anything not from the origin peer
+ if packet.PeerId() != p.id {
+ log.Debug("Received headers from incorrect peer", "peer", packet.PeerId())
+ break
+ }
+ // Make sure the peer actually gave something valid
+ headers := packet.(*headerPack).headers
+ if len(headers) == 0 {
+ p.log.Warn("Empty head header set")
+ return 0, errEmptyHeaderSet
+ }
+ // Make sure the peer's reply conforms to the request
+ for i, header := range headers {
+ expectNumber := from + int64(i)*int64(skip+1)
+ if number := header.Number.Int64(); number != expectNumber {
+ p.log.Warn("Head headers broke chain ordering", "index", i, "requested", expectNumber, "received", number)
+ return 0, fmt.Errorf("%w: %v", errInvalidChain, errors.New("head headers broke chain ordering"))
+ }
+ }
+ // Check if a common ancestor was found
+ finished = true
+ for i := len(headers) - 1; i >= 0; i-- {
+ // Skip any headers that underflow/overflow our requested set
+ if headers[i].Number.Int64() < from || headers[i].Number.Uint64() > max {
+ continue
+ }
+ // Otherwise check if we already know the header or not
+ h := headers[i].Hash()
+ n := headers[i].Number.Uint64()
+
+ var known bool
+ switch mode {
+ case FullSync:
+ known = d.blockchain.HasBlock(h, n)
+ case FastSync:
+ known = d.blockchain.HasFastBlock(h, n)
+ default:
+ known = d.lightchain.HasHeader(h, n)
+ }
+ if known {
+ number, hash = n, h
+ break
+ }
+ }
+
+ case <-timeout:
+ p.log.Debug("Waiting for head header timed out", "elapsed", ttl)
+ return 0, errTimeout
+
+ case <-d.bodyCh:
+ case <-d.receiptCh:
+ // Out of bounds delivery, ignore
+ }
+ }
+ // If the head fetch already found an ancestor, return
+ if hash != (common.Hash{}) {
+ if int64(number) <= floor {
+ p.log.Warn("Ancestor below allowance", "number", number, "hash", hash, "allowance", floor)
+ return 0, errInvalidAncestor
+ }
+ p.log.Debug("Found common ancestor", "number", number, "hash", hash)
+ return number, nil
+ }
+ return 0, errNoAncestorFound
+}
+
+func (d *Downloader) findAncestorBinarySearch(p *peerConnection, mode SyncMode, remoteHeight uint64, floor int64) (commonAncestor uint64, err error) {
+ hash := common.Hash{}
+
+ // Ancestor not found, we need to binary search over our chain
+ start, end := uint64(0), remoteHeight
+ if floor > 0 {
+ start = uint64(floor)
+ }
+ p.log.Trace("Binary searching for common ancestor", "start", start, "end", end)
+
+ for start+1 < end {
+ // Split our chain interval in two, and request the hash to cross check
+ check := (start + end) / 2
+
+ ttl := d.peers.rates.TargetTimeout()
+ timeout := time.After(ttl)
+
+ go p.peer.RequestHeadersByNumber(check, 1, 0, false)
+
+ // Wait until a reply arrives to this request
+ for arrived := false; !arrived; {
+ select {
+ case <-d.cancelCh:
+ return 0, errCanceled
+
+ case packet := <-d.headerCh:
+ // Discard anything not from the origin peer
+ if packet.PeerId() != p.id {
+ log.Debug("Received headers from incorrect peer", "peer", packet.PeerId())
+ break
+ }
+ // Make sure the peer actually gave something valid
+ headers := packet.(*headerPack).headers
+ if len(headers) != 1 {
+ p.log.Warn("Multiple headers for single request", "headers", len(headers))
+ return 0, fmt.Errorf("%w: multiple headers (%d) for single request", errBadPeer, len(headers))
+ }
+ arrived = true
+
+ // Modify the search interval based on the response
+ h := headers[0].Hash()
+ n := headers[0].Number.Uint64()
+
+ var known bool
+ switch mode {
+ case FullSync:
+ known = d.blockchain.HasBlock(h, n)
+ case FastSync:
+ known = d.blockchain.HasFastBlock(h, n)
+ default:
+ known = d.lightchain.HasHeader(h, n)
+ }
+ if !known {
+ end = check
+ break
+ }
+ header := d.lightchain.GetHeaderByHash(h) // Independent of sync mode, header surely exists
+ if header.Number.Uint64() != check {
+ p.log.Warn("Received non requested header", "number", header.Number, "hash", header.Hash(), "request", check)
+ return 0, fmt.Errorf("%w: non-requested header (%d)", errBadPeer, header.Number)
+ }
+ start = check
+ hash = h
+
+ case <-timeout:
+ p.log.Debug("Waiting for search header timed out", "elapsed", ttl)
+ return 0, errTimeout
+
+ case <-d.bodyCh:
+ case <-d.receiptCh:
+ // Out of bounds delivery, ignore
+ }
+ }
+ }
+ // Ensure valid ancestry and return
+ if int64(start) <= floor {
+ p.log.Warn("Ancestor below allowance", "number", start, "hash", hash, "allowance", floor)
+ return 0, errInvalidAncestor
+ }
+ p.log.Debug("Found common ancestor", "number", start, "hash", hash)
+ return start, nil
+}
+
+// fetchHeaders keeps retrieving headers concurrently from the number
+// requested, until no more are returned, potentially throttling on the way. To
+// facilitate concurrency but still protect against malicious nodes sending bad
+// headers, we construct a header chain skeleton using the "origin" peer we are
+// syncing with, and fill in the missing headers using anyone else. Headers from
+// other peers are only accepted if they map cleanly to the skeleton. If no one
+// can fill in the skeleton - not even the origin peer - it's assumed invalid and
+// the origin is dropped.
+func (d *Downloader) fetchHeaders(p *peerConnection, from uint64) error {
+ p.log.Debug("Directing header downloads", "origin", from)
+ defer p.log.Debug("Header download terminated")
+
+ // Create a timeout timer, and the associated header fetcher
+ skeleton := true // Skeleton assembly phase or finishing up
+ pivoting := false // Whether the next request is pivot verification
+ request := time.Now() // time of the last skeleton fetch request
+ timeout := time.NewTimer(0) // timer to dump a non-responsive active peer
+ <-timeout.C // timeout channel should be initially empty
+ defer timeout.Stop()
+
+ var ttl time.Duration
+ getHeaders := func(from uint64) {
+ request = time.Now()
+
+ ttl = d.peers.rates.TargetTimeout()
+ timeout.Reset(ttl)
+
+ if skeleton {
+ p.log.Trace("Fetching skeleton headers", "count", MaxHeaderFetch, "from", from)
+ go p.peer.RequestHeadersByNumber(from+uint64(MaxHeaderFetch)-1, MaxSkeletonSize, MaxHeaderFetch-1, false)
+ } else {
+ p.log.Trace("Fetching full headers", "count", MaxHeaderFetch, "from", from)
+ go p.peer.RequestHeadersByNumber(from, MaxHeaderFetch, 0, false)
+ }
+ }
+ getNextPivot := func() {
+ pivoting = true
+ request = time.Now()
+
+ ttl = d.peers.rates.TargetTimeout()
+ timeout.Reset(ttl)
+
+ d.pivotLock.RLock()
+ pivot := d.pivotHeader.Number.Uint64()
+ d.pivotLock.RUnlock()
+
+ p.log.Trace("Fetching next pivot header", "number", pivot+uint64(fsMinFullBlocks))
+ go p.peer.RequestHeadersByNumber(pivot+uint64(fsMinFullBlocks), 2, fsMinFullBlocks-9, false) // move +64 when it's 2x64-8 deep
+ }
+ // Start pulling the header chain skeleton until all is done
+ ancestor := from
+ getHeaders(from)
+
+ mode := d.getMode()
+ for {
+ select {
+ case <-d.cancelCh:
+ return errCanceled
+
+ case packet := <-d.headerCh:
+ // Make sure the active peer is giving us the skeleton headers
+ if packet.PeerId() != p.id {
+ log.Debug("Received skeleton from incorrect peer", "peer", packet.PeerId())
+ break
+ }
+ headerReqTimer.UpdateSince(request)
+ timeout.Stop()
+
+ // If the pivot is being checked, move if it became stale and run the real retrieval
+ var pivot uint64
+
+ d.pivotLock.RLock()
+ if d.pivotHeader != nil {
+ pivot = d.pivotHeader.Number.Uint64()
+ }
+ d.pivotLock.RUnlock()
+
+ if pivoting {
+ if packet.Items() == 2 {
+ // Retrieve the headers and do some sanity checks, just in case
+ headers := packet.(*headerPack).headers
+
+ if have, want := headers[0].Number.Uint64(), pivot+uint64(fsMinFullBlocks); have != want {
+ log.Warn("Peer sent invalid next pivot", "have", have, "want", want)
+ return fmt.Errorf("%w: next pivot number %d != requested %d", errInvalidChain, have, want)
+ }
+ if have, want := headers[1].Number.Uint64(), pivot+2*uint64(fsMinFullBlocks)-8; have != want {
+ log.Warn("Peer sent invalid pivot confirmer", "have", have, "want", want)
+ return fmt.Errorf("%w: next pivot confirmer number %d != requested %d", errInvalidChain, have, want)
+ }
+ log.Warn("Pivot seemingly stale, moving", "old", pivot, "new", headers[0].Number)
+ pivot = headers[0].Number.Uint64()
+
+ d.pivotLock.Lock()
+ d.pivotHeader = headers[0]
+ d.pivotLock.Unlock()
+
+ // Write out the pivot into the database so a rollback beyond
+ // it will reenable fast sync and update the state root that
+ // the state syncer will be downloading.
+ rawdb.WriteLastPivotNumber(d.stateDB, pivot)
+ }
+ pivoting = false
+ getHeaders(from)
+ continue
+ }
+ // If the skeleton's finished, pull any remaining head headers directly from the origin
+ if skeleton && packet.Items() == 0 {
+ skeleton = false
+ getHeaders(from)
+ continue
+ }
+ // If no more headers are inbound, notify the content fetchers and return
+ if packet.Items() == 0 {
+ // Don't abort header fetches while the pivot is downloading
+ if atomic.LoadInt32(&d.committed) == 0 && pivot <= from {
+ p.log.Debug("No headers, waiting for pivot commit")
+ select {
+ case <-time.After(fsHeaderContCheck):
+ getHeaders(from)
+ continue
+ case <-d.cancelCh:
+ return errCanceled
+ }
+ }
+ // Pivot done (or not in fast sync) and no more headers, terminate the process
+ p.log.Debug("No more headers available")
+ select {
+ case d.headerProcCh <- nil:
+ return nil
+ case <-d.cancelCh:
+ return errCanceled
+ }
+ }
+ headers := packet.(*headerPack).headers
+
+ // If we received a skeleton batch, resolve internals concurrently
+ if skeleton {
+ filled, proced, err := d.fillHeaderSkeleton(from, headers)
+ if err != nil {
+ p.log.Debug("Skeleton chain invalid", "err", err)
+ return fmt.Errorf("%w: %v", errInvalidChain, err)
+ }
+ headers = filled[proced:]
+ from += uint64(proced)
+ } else {
+ // If we're closing in on the chain head, but haven't yet reached it, delay
+ // the last few headers so mini reorgs on the head don't cause invalid hash
+ // chain errors.
+ if n := len(headers); n > 0 {
+ // Retrieve the current head we're at
+ var head uint64
+ if mode == LightSync {
+ head = d.lightchain.CurrentHeader().Number.Uint64()
+ } else {
+ head = d.blockchain.CurrentFastBlock().NumberU64()
+ if full := d.blockchain.CurrentBlock().NumberU64(); head < full {
+ head = full
+ }
+ }
+ // If the head is below the common ancestor, we're actually deduplicating
+ // already existing chain segments, so use the ancestor as the fake head.
+ // Otherwise we might end up delaying header deliveries pointlessly.
+ if head < ancestor {
+ head = ancestor
+ }
+ // If the head is way older than this batch, delay the last few headers
+ if head+uint64(reorgProtThreshold) < headers[n-1].Number.Uint64() {
+ delay := reorgProtHeaderDelay
+ if delay > n {
+ delay = n
+ }
+ headers = headers[:n-delay]
+ }
+ }
+ }
+ // Insert all the new headers and fetch the next batch
+ if len(headers) > 0 {
+ p.log.Trace("Scheduling new headers", "count", len(headers), "from", from)
+ select {
+ case d.headerProcCh <- headers:
+ case <-d.cancelCh:
+ return errCanceled
+ }
+ from += uint64(len(headers))
+
+ // If we're still skeleton filling fast sync, check pivot staleness
+ // before continuing to the next skeleton filling
+ if skeleton && pivot > 0 {
+ getNextPivot()
+ } else {
+ getHeaders(from)
+ }
+ } else {
+ // No headers delivered, or all of them being delayed, sleep a bit and retry
+ p.log.Trace("All headers delayed, waiting")
+ select {
+ case <-time.After(fsHeaderContCheck):
+ getHeaders(from)
+ continue
+ case <-d.cancelCh:
+ return errCanceled
+ }
+ }
+
+ case <-timeout.C:
+ if d.dropPeer == nil {
+ // The dropPeer method is nil when `--copydb` is used for a local copy.
+ // Timeouts can occur if e.g. compaction hits at the wrong time, and can be ignored
+ p.log.Warn("Downloader wants to drop peer, but peerdrop-function is not set", "peer", p.id)
+ break
+ }
+ // Header retrieval timed out, consider the peer bad and drop
+ p.log.Debug("Header request timed out", "elapsed", ttl)
+ headerTimeoutMeter.Mark(1)
+ d.dropPeer(p.id)
+
+ // Finish the sync gracefully instead of dumping the gathered data though
+ for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh} {
+ select {
+ case ch <- false:
+ case <-d.cancelCh:
+ }
+ }
+ select {
+ case d.headerProcCh <- nil:
+ case <-d.cancelCh:
+ }
+ return fmt.Errorf("%w: header request timed out", errBadPeer)
+ }
+ }
+}
+
+// fillHeaderSkeleton concurrently retrieves headers from all our available peers
+// and maps them to the provided skeleton header chain.
+//
+// Any partial results from the beginning of the skeleton is (if possible) forwarded
+// immediately to the header processor to keep the rest of the pipeline full even
+// in the case of header stalls.
+//
+// The method returns the entire filled skeleton and also the number of headers
+// already forwarded for processing.
+func (d *Downloader) fillHeaderSkeleton(from uint64, skeleton []*types.Header) ([]*types.Header, int, error) {
+ log.Debug("Filling up skeleton", "from", from)
+ d.queue.ScheduleSkeleton(from, skeleton)
+
+ var (
+ deliver = func(packet dataPack) (int, error) {
+ pack := packet.(*headerPack)
+ return d.queue.DeliverHeaders(pack.peerID, pack.headers, d.headerProcCh)
+ }
+ expire = func() map[string]int { return d.queue.ExpireHeaders(d.peers.rates.TargetTimeout()) }
+ reserve = func(p *peerConnection, count int) (*fetchRequest, bool, bool) {
+ return d.queue.ReserveHeaders(p, count), false, false
+ }
+ fetch = func(p *peerConnection, req *fetchRequest) error { return p.FetchHeaders(req.From, MaxHeaderFetch) }
+ capacity = func(p *peerConnection) int { return p.HeaderCapacity(d.peers.rates.TargetRoundTrip()) }
+ setIdle = func(p *peerConnection, accepted int, deliveryTime time.Time) {
+ p.SetHeadersIdle(accepted, deliveryTime)
+ }
+ )
+ err := d.fetchParts(d.headerCh, deliver, d.queue.headerContCh, expire,
+ d.queue.PendingHeaders, d.queue.InFlightHeaders, reserve,
+ nil, fetch, d.queue.CancelHeaders, capacity, d.peers.HeaderIdlePeers, setIdle, "headers")
+
+ log.Debug("Skeleton fill terminated", "err", err)
+
+ filled, proced := d.queue.RetrieveHeaders()
+ return filled, proced, err
+}
+
+// fetchBodies iteratively downloads the scheduled block bodies, taking any
+// available peers, reserving a chunk of blocks for each, waiting for delivery
+// and also periodically checking for timeouts.
+func (d *Downloader) fetchBodies(from uint64) error {
+ log.Debug("Downloading block bodies", "origin", from)
+
+ var (
+ deliver = func(packet dataPack) (int, error) {
+ pack := packet.(*bodyPack)
+ return d.queue.DeliverBodies(pack.peerID, pack.transactions, pack.uncles)
+ }
+ expire = func() map[string]int { return d.queue.ExpireBodies(d.peers.rates.TargetTimeout()) }
+ fetch = func(p *peerConnection, req *fetchRequest) error { return p.FetchBodies(req) }
+ capacity = func(p *peerConnection) int { return p.BlockCapacity(d.peers.rates.TargetRoundTrip()) }
+ setIdle = func(p *peerConnection, accepted int, deliveryTime time.Time) { p.SetBodiesIdle(accepted, deliveryTime) }
+ )
+ err := d.fetchParts(d.bodyCh, deliver, d.bodyWakeCh, expire,
+ d.queue.PendingBlocks, d.queue.InFlightBlocks, d.queue.ReserveBodies,
+ d.bodyFetchHook, fetch, d.queue.CancelBodies, capacity, d.peers.BodyIdlePeers, setIdle, "bodies")
+
+ log.Debug("Block body download terminated", "err", err)
+ return err
+}
+
+// fetchReceipts iteratively downloads the scheduled block receipts, taking any
+// available peers, reserving a chunk of receipts for each, waiting for delivery
+// and also periodically checking for timeouts.
+func (d *Downloader) fetchReceipts(from uint64) error {
+ log.Debug("Downloading transaction receipts", "origin", from)
+
+ var (
+ deliver = func(packet dataPack) (int, error) {
+ pack := packet.(*receiptPack)
+ return d.queue.DeliverReceipts(pack.peerID, pack.receipts)
+ }
+ expire = func() map[string]int { return d.queue.ExpireReceipts(d.peers.rates.TargetTimeout()) }
+ fetch = func(p *peerConnection, req *fetchRequest) error { return p.FetchReceipts(req) }
+ capacity = func(p *peerConnection) int { return p.ReceiptCapacity(d.peers.rates.TargetRoundTrip()) }
+ setIdle = func(p *peerConnection, accepted int, deliveryTime time.Time) {
+ p.SetReceiptsIdle(accepted, deliveryTime)
+ }
+ )
+ err := d.fetchParts(d.receiptCh, deliver, d.receiptWakeCh, expire,
+ d.queue.PendingReceipts, d.queue.InFlightReceipts, d.queue.ReserveReceipts,
+ d.receiptFetchHook, fetch, d.queue.CancelReceipts, capacity, d.peers.ReceiptIdlePeers, setIdle, "receipts")
+
+ log.Debug("Transaction receipt download terminated", "err", err)
+ return err
+}
+
+// fetchParts iteratively downloads scheduled block parts, taking any available
+// peers, reserving a chunk of fetch requests for each, waiting for delivery and
+// also periodically checking for timeouts.
+//
+// As the scheduling/timeout logic mostly is the same for all downloaded data
+// types, this method is used by each for data gathering and is instrumented with
+// various callbacks to handle the slight differences between processing them.
+//
+// The instrumentation parameters:
+// - errCancel: error type to return if the fetch operation is cancelled (mostly makes logging nicer)
+// - deliveryCh: channel from which to retrieve downloaded data packets (merged from all concurrent peers)
+// - deliver: processing callback to deliver data packets into type specific download queues (usually within `queue`)
+// - wakeCh: notification channel for waking the fetcher when new tasks are available (or sync completed)
+// - expire: task callback method to abort requests that took too long and return the faulty peers (traffic shaping)
+// - pending: task callback for the number of requests still needing download (detect completion/non-completability)
+// - inFlight: task callback for the number of in-progress requests (wait for all active downloads to finish)
+// - throttle: task callback to check if the processing queue is full and activate throttling (bound memory use)
+// - reserve: task callback to reserve new download tasks to a particular peer (also signals partial completions)
+// - fetchHook: tester callback to notify of new tasks being initiated (allows testing the scheduling logic)
+// - fetch: network callback to actually send a particular download request to a physical remote peer
+// - cancel: task callback to abort an in-flight download request and allow rescheduling it (in case of lost peer)
+// - capacity: network callback to retrieve the estimated type-specific bandwidth capacity of a peer (traffic shaping)
+// - idle: network callback to retrieve the currently (type specific) idle peers that can be assigned tasks
+// - setIdle: network callback to set a peer back to idle and update its estimated capacity (traffic shaping)
+// - kind: textual label of the type being downloaded to display in log messages
+func (d *Downloader) fetchParts(deliveryCh chan dataPack, deliver func(dataPack) (int, error), wakeCh chan bool,
+ expire func() map[string]int, pending func() int, inFlight func() bool, reserve func(*peerConnection, int) (*fetchRequest, bool, bool),
+ fetchHook func([]*types.Header), fetch func(*peerConnection, *fetchRequest) error, cancel func(*fetchRequest), capacity func(*peerConnection) int,
+ idle func() ([]*peerConnection, int), setIdle func(*peerConnection, int, time.Time), kind string) error {
+
+ // Create a ticker to detect expired retrieval tasks
+ ticker := time.NewTicker(100 * time.Millisecond)
+ defer ticker.Stop()
+
+ update := make(chan struct{}, 1)
+
+ // Prepare the queue and fetch block parts until the block header fetcher's done
+ finished := false
+ for {
+ select {
+ case <-d.cancelCh:
+ return errCanceled
+
+ case packet := <-deliveryCh:
+ deliveryTime := time.Now()
+ // If the peer was previously banned and failed to deliver its pack
+ // in a reasonable time frame, ignore its message.
+ if peer := d.peers.Peer(packet.PeerId()); peer != nil {
+ // Deliver the received chunk of data and check chain validity
+ accepted, err := deliver(packet)
+ if errors.Is(err, errInvalidChain) {
+ return err
+ }
+ // Unless a peer delivered something completely else than requested (usually
+ // caused by a timed out request which came through in the end), set it to
+ // idle. If the delivery's stale, the peer should have already been idled.
+ if !errors.Is(err, errStaleDelivery) {
+ setIdle(peer, accepted, deliveryTime)
+ }
+ // Issue a log to the user to see what's going on
+ switch {
+ case err == nil && packet.Items() == 0:
+ peer.log.Trace("Requested data not delivered", "type", kind)
+ case err == nil:
+ peer.log.Trace("Delivered new batch of data", "type", kind, "count", packet.Stats())
+ default:
+ peer.log.Debug("Failed to deliver retrieved data", "type", kind, "err", err)
+ }
+ }
+ // Blocks assembled, try to update the progress
+ select {
+ case update <- struct{}{}:
+ default:
+ }
+
+ case cont := <-wakeCh:
+ // The header fetcher sent a continuation flag, check if it's done
+ if !cont {
+ finished = true
+ }
+ // Headers arrive, try to update the progress
+ select {
+ case update <- struct{}{}:
+ default:
+ }
+
+ case <-ticker.C:
+ // Sanity check update the progress
+ select {
+ case update <- struct{}{}:
+ default:
+ }
+
+ case <-update:
+ // Short circuit if we lost all our peers
+ if d.peers.Len() == 0 {
+ return errNoPeers
+ }
+ // Check for fetch request timeouts and demote the responsible peers
+ for pid, fails := range expire() {
+ if peer := d.peers.Peer(pid); peer != nil {
+ // If a lot of retrieval elements expired, we might have overestimated the remote peer or perhaps
+ // ourselves. Only reset to minimal throughput but don't drop just yet. If even the minimal times
+ // out that sync wise we need to get rid of the peer.
+ //
+ // The reason the minimum threshold is 2 is because the downloader tries to estimate the bandwidth
+ // and latency of a peer separately, which requires pushing the measures capacity a bit and seeing
+ // how response times reacts, to it always requests one more than the minimum (i.e. min 2).
+ if fails > 2 {
+ peer.log.Trace("Data delivery timed out", "type", kind)
+ setIdle(peer, 0, time.Now())
+ } else {
+ peer.log.Debug("Stalling delivery, dropping", "type", kind)
+
+ if d.dropPeer == nil {
+ // The dropPeer method is nil when `--copydb` is used for a local copy.
+ // Timeouts can occur if e.g. compaction hits at the wrong time, and can be ignored
+ peer.log.Warn("Downloader wants to drop peer, but peerdrop-function is not set", "peer", pid)
+ } else {
+ d.dropPeer(pid)
+
+ // If this peer was the master peer, abort sync immediately
+ d.cancelLock.RLock()
+ master := pid == d.cancelPeer
+ d.cancelLock.RUnlock()
+
+ if master {
+ d.cancel()
+ return errTimeout
+ }
+ }
+ }
+ }
+ }
+ // If there's nothing more to fetch, wait or terminate
+ if pending() == 0 {
+ if !inFlight() && finished {
+ log.Debug("Data fetching completed", "type", kind)
+ return nil
+ }
+ break
+ }
+ // Send a download request to all idle peers, until throttled
+ progressed, throttled, running := false, false, inFlight()
+ idles, total := idle()
+ pendCount := pending()
+ for _, peer := range idles {
+ // Short circuit if throttling activated
+ if throttled {
+ break
+ }
+ // Short circuit if there is no more available task.
+ if pendCount = pending(); pendCount == 0 {
+ break
+ }
+ // Reserve a chunk of fetches for a peer. A nil can mean either that
+ // no more headers are available, or that the peer is known not to
+ // have them.
+ request, progress, throttle := reserve(peer, capacity(peer))
+ if progress {
+ progressed = true
+ }
+ if throttle {
+ throttled = true
+ throttleCounter.Inc(1)
+ }
+ if request == nil {
+ continue
+ }
+ if request.From > 0 {
+ peer.log.Trace("Requesting new batch of data", "type", kind, "from", request.From)
+ } else {
+ peer.log.Trace("Requesting new batch of data", "type", kind, "count", len(request.Headers), "from", request.Headers[0].Number)
+ }
+ // Fetch the chunk and make sure any errors return the hashes to the queue
+ if fetchHook != nil {
+ fetchHook(request.Headers)
+ }
+ if err := fetch(peer, request); err != nil {
+ // Although we could try and make an attempt to fix this, this error really
+ // means that we've double allocated a fetch task to a peer. If that is the
+ // case, the internal state of the downloader and the queue is very wrong so
+ // better hard crash and note the error instead of silently accumulating into
+ // a much bigger issue.
+ panic(fmt.Sprintf("%v: %s fetch assignment failed", peer, kind))
+ }
+ running = true
+ }
+ // Make sure that we have peers available for fetching. If all peers have been tried
+ // and all failed throw an error
+ if !progressed && !throttled && !running && len(idles) == total && pendCount > 0 {
+ return errPeersUnavailable
+ }
+ }
+ }
+}
+
+// processHeaders takes batches of retrieved headers from an input channel and
+// keeps processing and scheduling them into the header chain and downloader's
+// queue until the stream ends or a failure occurs.
+func (d *Downloader) processHeaders(origin uint64, td *big.Int) error {
+ // Keep a count of uncertain headers to roll back
+ var (
+ rollback uint64 // Zero means no rollback (fine as you can't unroll the genesis)
+ rollbackErr error
+ mode = d.getMode()
+ )
+ defer func() {
+ if rollback > 0 {
+ lastHeader, lastFastBlock, lastBlock := d.lightchain.CurrentHeader().Number, common.Big0, common.Big0
+ if mode != LightSync {
+ lastFastBlock = d.blockchain.CurrentFastBlock().Number()
+ lastBlock = d.blockchain.CurrentBlock().Number()
+ }
+ if err := d.lightchain.SetHead(rollback - 1); err != nil { // -1 to target the parent of the first uncertain block
+ // We're already unwinding the stack, only print the error to make it more visible
+ log.Error("Failed to roll back chain segment", "head", rollback-1, "err", err)
+ }
+ curFastBlock, curBlock := common.Big0, common.Big0
+ if mode != LightSync {
+ curFastBlock = d.blockchain.CurrentFastBlock().Number()
+ curBlock = d.blockchain.CurrentBlock().Number()
+ }
+ log.Warn("Rolled back chain segment",
+ "header", fmt.Sprintf("%d->%d", lastHeader, d.lightchain.CurrentHeader().Number),
+ "fast", fmt.Sprintf("%d->%d", lastFastBlock, curFastBlock),
+ "block", fmt.Sprintf("%d->%d", lastBlock, curBlock), "reason", rollbackErr)
+ }
+ }()
+ // Wait for batches of headers to process
+ gotHeaders := false
+
+ for {
+ select {
+ case <-d.cancelCh:
+ rollbackErr = errCanceled
+ return errCanceled
+
+ case headers := <-d.headerProcCh:
+ // Terminate header processing if we synced up
+ if len(headers) == 0 {
+ // Notify everyone that headers are fully processed
+ for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh} {
+ select {
+ case ch <- false:
+ case <-d.cancelCh:
+ }
+ }
+ // If no headers were retrieved at all, the peer violated its TD promise that it had a
+ // better chain compared to ours. The only exception is if its promised blocks were
+ // already imported by other means (e.g. fetcher):
+ //
+ // R , L : Both at block 10
+ // R: Mine block 11, and propagate it to L
+ // L: Queue block 11 for import
+ // L: Notice that R's head and TD increased compared to ours, start sync
+ // L: Import of block 11 finishes
+ // L: Sync begins, and finds common ancestor at 11
+ // L: Request new headers up from 11 (R's TD was higher, it must have something)
+ // R: Nothing to give
+ if mode != LightSync {
+ head := d.blockchain.CurrentBlock()
+ if !gotHeaders && td.Cmp(d.blockchain.GetTd(head.Hash(), head.NumberU64())) > 0 {
+ return errStallingPeer
+ }
+ }
+ // If fast or light syncing, ensure promised headers are indeed delivered. This is
+ // needed to detect scenarios where an attacker feeds a bad pivot and then bails out
+ // of delivering the post-pivot blocks that would flag the invalid content.
+ //
+ // This check cannot be executed "as is" for full imports, since blocks may still be
+ // queued for processing when the header download completes. However, as long as the
+ // peer gave us something useful, we're already happy/progressed (above check).
+ if mode == FastSync || mode == LightSync {
+ head := d.lightchain.CurrentHeader()
+ if td.Cmp(d.lightchain.GetTd(head.Hash(), head.Number.Uint64())) > 0 {
+ return errStallingPeer
+ }
+ }
+ // Disable any rollback and return
+ rollback = 0
+ return nil
+ }
+ // Otherwise split the chunk of headers into batches and process them
+ gotHeaders = true
+ for len(headers) > 0 {
+ // Terminate if something failed in between processing chunks
+ select {
+ case <-d.cancelCh:
+ rollbackErr = errCanceled
+ return errCanceled
+ default:
+ }
+ // Select the next chunk of headers to import
+ limit := maxHeadersProcess
+ if limit > len(headers) {
+ limit = len(headers)
+ }
+ chunk := headers[:limit]
+
+ // In case of header only syncing, validate the chunk immediately
+ if mode == FastSync || mode == LightSync {
+ // If we're importing pure headers, verify based on their recentness
+ var pivot uint64
+
+ d.pivotLock.RLock()
+ if d.pivotHeader != nil {
+ pivot = d.pivotHeader.Number.Uint64()
+ }
+ d.pivotLock.RUnlock()
+
+ frequency := fsHeaderCheckFrequency
+ if chunk[len(chunk)-1].Number.Uint64()+uint64(fsHeaderForceVerify) > pivot {
+ frequency = 1
+ }
+ if n, err := d.lightchain.InsertHeaderChain(chunk, frequency); err != nil {
+ rollbackErr = err
+
+ // If some headers were inserted, track them as uncertain
+ if (mode == FastSync || frequency > 1) && n > 0 && rollback == 0 {
+ rollback = chunk[0].Number.Uint64()
+ }
+ log.Warn("Invalid header encountered", "number", chunk[n].Number, "hash", chunk[n].Hash(), "parent", chunk[n].ParentHash, "err", err)
+ return fmt.Errorf("%w: %v", errInvalidChain, err)
+ }
+ // All verifications passed, track all headers within the alloted limits
+ if mode == FastSync {
+ head := chunk[len(chunk)-1].Number.Uint64()
+ if head-rollback > uint64(fsHeaderSafetyNet) {
+ rollback = head - uint64(fsHeaderSafetyNet)
+ } else {
+ rollback = 1
+ }
+ }
+ }
+ // Unless we're doing light chains, schedule the headers for associated content retrieval
+ if mode == FullSync || mode == FastSync {
+ // If we've reached the allowed number of pending headers, stall a bit
+ for d.queue.PendingBlocks() >= maxQueuedHeaders || d.queue.PendingReceipts() >= maxQueuedHeaders {
+ select {
+ case <-d.cancelCh:
+ rollbackErr = errCanceled
+ return errCanceled
+ case <-time.After(time.Second):
+ }
+ }
+ // Otherwise insert the headers for content retrieval
+ inserts := d.queue.Schedule(chunk, origin)
+ if len(inserts) != len(chunk) {
+ rollbackErr = fmt.Errorf("stale headers: len inserts %v len(chunk) %v", len(inserts), len(chunk))
+ return fmt.Errorf("%w: stale headers", errBadPeer)
+ }
+ }
+ headers = headers[limit:]
+ origin += uint64(limit)
+ }
+ // Update the highest block number we know if a higher one is found.
+ d.syncStatsLock.Lock()
+ if d.syncStatsChainHeight < origin {
+ d.syncStatsChainHeight = origin - 1
+ }
+ d.syncStatsLock.Unlock()
+
+ // Signal the content downloaders of the availablility of new tasks
+ for _, ch := range []chan bool{d.bodyWakeCh, d.receiptWakeCh} {
+ select {
+ case ch <- true:
+ default:
+ }
+ }
+ }
+ }
+}
+
+// processFullSyncContent takes fetch results from the queue and imports them into the chain.
+func (d *Downloader) processFullSyncContent() error {
+ for {
+ results := d.queue.Results(true)
+ if len(results) == 0 {
+ return nil
+ }
+ if d.chainInsertHook != nil {
+ d.chainInsertHook(results)
+ }
+ if err := d.importBlockResults(results); err != nil {
+ return err
+ }
+ }
+}
+
+func (d *Downloader) importBlockResults(results []*fetchResult) error {
+ // Check for any early termination requests
+ if len(results) == 0 {
+ return nil
+ }
+ select {
+ case <-d.quitCh:
+ return errCancelContentProcessing
+ default:
+ }
+ // Retrieve the a batch of results to import
+ first, last := results[0].Header, results[len(results)-1].Header
+ log.Debug("Inserting downloaded chain", "items", len(results),
+ "firstnum", first.Number, "firsthash", first.Hash(),
+ "lastnum", last.Number, "lasthash", last.Hash(),
+ )
+ blocks := make([]*types.Block, len(results))
+ for i, result := range results {
+ blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
+ }
+ if index, err := d.blockchain.InsertChain(blocks); err != nil {
+ if index < len(results) {
+ log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err)
+ } else {
+ // The InsertChain method in blockchain.go will sometimes return an out-of-bounds index,
+ // when it needs to preprocess blocks to import a sidechain.
+ // The importer will put together a new list of blocks to import, which is a superset
+ // of the blocks delivered from the downloader, and the indexing will be off.
+ log.Debug("Downloaded item processing failed on sidechain import", "index", index, "err", err)
+ }
+ return fmt.Errorf("%w: %v", errInvalidChain, err)
+ }
+ return nil
+}
+
+// processFastSyncContent takes fetch results from the queue and writes them to the
+// database. It also controls the synchronisation of state nodes of the pivot block.
+func (d *Downloader) processFastSyncContent() error {
+ // Start syncing state of the reported head block. This should get us most of
+ // the state of the pivot block.
+ d.pivotLock.RLock()
+ sync := d.syncState(d.pivotHeader.Root)
+ d.pivotLock.RUnlock()
+
+ defer func() {
+ // The `sync` object is replaced every time the pivot moves. We need to
+ // defer close the very last active one, hence the lazy evaluation vs.
+ // calling defer sync.Cancel() !!!
+ sync.Cancel()
+ }()
+
+ closeOnErr := func(s *stateSync) {
+ if err := s.Wait(); err != nil && err != errCancelStateFetch && err != errCanceled && err != snap.ErrCancelled {
+ d.queue.Close() // wake up Results
+ }
+ }
+ go closeOnErr(sync)
+
+ // To cater for moving pivot points, track the pivot block and subsequently
+ // accumulated download results separately.
+ var (
+ oldPivot *fetchResult // Locked in pivot block, might change eventually
+ oldTail []*fetchResult // Downloaded content after the pivot
+ )
+ for {
+ // Wait for the next batch of downloaded data to be available, and if the pivot
+ // block became stale, move the goalpost
+ results := d.queue.Results(oldPivot == nil) // Block if we're not monitoring pivot staleness
+ if len(results) == 0 {
+ // If pivot sync is done, stop
+ if oldPivot == nil {
+ return sync.Cancel()
+ }
+ // If sync failed, stop
+ select {
+ case <-d.cancelCh:
+ sync.Cancel()
+ return errCanceled
+ default:
+ }
+ }
+ if d.chainInsertHook != nil {
+ d.chainInsertHook(results)
+ }
+ // If we haven't downloaded the pivot block yet, check pivot staleness
+ // notifications from the header downloader
+ d.pivotLock.RLock()
+ pivot := d.pivotHeader
+ d.pivotLock.RUnlock()
+
+ if oldPivot == nil {
+ if pivot.Root != sync.root {
+ sync.Cancel()
+ sync = d.syncState(pivot.Root)
+
+ go closeOnErr(sync)
+ }
+ } else {
+ results = append(append([]*fetchResult{oldPivot}, oldTail...), results...)
+ }
+ // Split around the pivot block and process the two sides via fast/full sync
+ if atomic.LoadInt32(&d.committed) == 0 {
+ latest := results[len(results)-1].Header
+ // If the height is above the pivot block by 2 sets, it means the pivot
+ // become stale in the network and it was garbage collected, move to a
+ // new pivot.
+ //
+ // Note, we have `reorgProtHeaderDelay` number of blocks withheld, Those
+ // need to be taken into account, otherwise we're detecting the pivot move
+ // late and will drop peers due to unavailable state!!!
+ if height := latest.Number.Uint64(); height >= pivot.Number.Uint64()+2*uint64(fsMinFullBlocks)-uint64(reorgProtHeaderDelay) {
+ log.Warn("Pivot became stale, moving", "old", pivot.Number.Uint64(), "new", height-uint64(fsMinFullBlocks)+uint64(reorgProtHeaderDelay))
+ pivot = results[len(results)-1-fsMinFullBlocks+reorgProtHeaderDelay].Header // must exist as lower old pivot is uncommitted
+
+ d.pivotLock.Lock()
+ d.pivotHeader = pivot
+ d.pivotLock.Unlock()
+
+ // Write out the pivot into the database so a rollback beyond it will
+ // reenable fast sync
+ rawdb.WriteLastPivotNumber(d.stateDB, pivot.Number.Uint64())
+ }
+ }
+ P, beforeP, afterP := splitAroundPivot(pivot.Number.Uint64(), results)
+ if err := d.commitFastSyncData(beforeP, sync); err != nil {
+ return err
+ }
+ if P != nil {
+ // If new pivot block found, cancel old state retrieval and restart
+ if oldPivot != P {
+ sync.Cancel()
+ sync = d.syncState(P.Header.Root)
+
+ go closeOnErr(sync)
+ oldPivot = P
+ }
+ // Wait for completion, occasionally checking for pivot staleness
+ select {
+ case <-sync.done:
+ if sync.err != nil {
+ return sync.err
+ }
+ if err := d.commitPivotBlock(P); err != nil {
+ return err
+ }
+ oldPivot = nil
+
+ case <-time.After(time.Second):
+ oldTail = afterP
+ continue
+ }
+ }
+ // Fast sync done, pivot commit done, full import
+ if err := d.importBlockResults(afterP); err != nil {
+ return err
+ }
+ }
+}
+
+func splitAroundPivot(pivot uint64, results []*fetchResult) (p *fetchResult, before, after []*fetchResult) {
+ if len(results) == 0 {
+ return nil, nil, nil
+ }
+ if lastNum := results[len(results)-1].Header.Number.Uint64(); lastNum < pivot {
+ // the pivot is somewhere in the future
+ return nil, results, nil
+ }
+ // This can also be optimized, but only happens very seldom
+ for _, result := range results {
+ num := result.Header.Number.Uint64()
+ switch {
+ case num < pivot:
+ before = append(before, result)
+ case num == pivot:
+ p = result
+ default:
+ after = append(after, result)
+ }
+ }
+ return p, before, after
+}
+
+func (d *Downloader) commitFastSyncData(results []*fetchResult, stateSync *stateSync) error {
+ // Check for any early termination requests
+ if len(results) == 0 {
+ return nil
+ }
+ select {
+ case <-d.quitCh:
+ return errCancelContentProcessing
+ case <-stateSync.done:
+ if err := stateSync.Wait(); err != nil {
+ return err
+ }
+ default:
+ }
+ // Retrieve the a batch of results to import
+ first, last := results[0].Header, results[len(results)-1].Header
+ log.Debug("Inserting fast-sync blocks", "items", len(results),
+ "firstnum", first.Number, "firsthash", first.Hash(),
+ "lastnumn", last.Number, "lasthash", last.Hash(),
+ )
+ blocks := make([]*types.Block, len(results))
+ receipts := make([]types.Receipts, len(results))
+ for i, result := range results {
+ blocks[i] = types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
+ receipts[i] = result.Receipts
+ }
+ if index, err := d.blockchain.InsertReceiptChain(blocks, receipts, d.ancientLimit); err != nil {
+ log.Debug("Downloaded item processing failed", "number", results[index].Header.Number, "hash", results[index].Header.Hash(), "err", err)
+ return fmt.Errorf("%w: %v", errInvalidChain, err)
+ }
+ return nil
+}
+
+func (d *Downloader) commitPivotBlock(result *fetchResult) error {
+ block := types.NewBlockWithHeader(result.Header).WithBody(result.Transactions, result.Uncles)
+ log.Debug("Committing fast sync pivot as new head", "number", block.Number(), "hash", block.Hash())
+
+ // Commit the pivot block as the new head, will require full sync from here on
+ if _, err := d.blockchain.InsertReceiptChain([]*types.Block{block}, []types.Receipts{result.Receipts}, d.ancientLimit); err != nil {
+ return err
+ }
+ if err := d.blockchain.FastSyncCommitHead(block.Hash()); err != nil {
+ return err
+ }
+ atomic.StoreInt32(&d.committed, 1)
+
+ // If we had a bloom filter for the state sync, deallocate it now. Note, we only
+ // deallocate internally, but keep the empty wrapper. This ensures that if we do
+ // a rollback after committing the pivot and restarting fast sync, we don't end
+ // up using a nil bloom. Empty bloom is fine, it just returns that it does not
+ // have the info we need, so reach down to the database instead.
+ if d.stateBloom != nil {
+ d.stateBloom.Close()
+ }
+ return nil
+}
+
+// DeliverHeaders injects a new batch of block headers received from a remote
+// node into the download schedule.
+func (d *Downloader) DeliverHeaders(id string, headers []*types.Header) error {
+ return d.deliver(d.headerCh, &headerPack{id, headers}, headerInMeter, headerDropMeter)
+}
+
+// DeliverBodies injects a new batch of block bodies received from a remote node.
+func (d *Downloader) DeliverBodies(id string, transactions [][]*types.Transaction, uncles [][]*types.Header) error {
+ return d.deliver(d.bodyCh, &bodyPack{id, transactions, uncles}, bodyInMeter, bodyDropMeter)
+}
+
+// DeliverReceipts injects a new batch of receipts received from a remote node.
+func (d *Downloader) DeliverReceipts(id string, receipts [][]*types.Receipt) error {
+ return d.deliver(d.receiptCh, &receiptPack{id, receipts}, receiptInMeter, receiptDropMeter)
+}
+
+// DeliverNodeData injects a new batch of node state data received from a remote node.
+func (d *Downloader) DeliverNodeData(id string, data [][]byte) error {
+ return d.deliver(d.stateCh, &statePack{id, data}, stateInMeter, stateDropMeter)
+}
+
+// DeliverSnapPacket is invoked from a peer's message handler when it transmits a
+// data packet for the local node to consume.
+func (d *Downloader) DeliverSnapPacket(peer *snap.Peer, packet snap.Packet) error {
+ switch packet := packet.(type) {
+ case *snap.AccountRangePacket:
+ hashes, accounts, err := packet.Unpack()
+ if err != nil {
+ return err
+ }
+ return d.SnapSyncer.OnAccounts(peer, packet.ID, hashes, accounts, packet.Proof)
+
+ case *snap.StorageRangesPacket:
+ hashset, slotset := packet.Unpack()
+ return d.SnapSyncer.OnStorage(peer, packet.ID, hashset, slotset, packet.Proof)
+
+ case *snap.ByteCodesPacket:
+ return d.SnapSyncer.OnByteCodes(peer, packet.ID, packet.Codes)
+
+ case *snap.TrieNodesPacket:
+ return d.SnapSyncer.OnTrieNodes(peer, packet.ID, packet.Nodes)
+
+ default:
+ return fmt.Errorf("unexpected snap packet type: %T", packet)
+ }
+}
+
+// deliver injects a new batch of data received from a remote node.
+func (d *Downloader) deliver(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() {
+ if err != nil {
+ dropMeter.Mark(int64(packet.Items()))
+ }
+ }()
+ // Deliver or abort if the sync is canceled while queuing
+ d.cancelLock.RLock()
+ cancel := d.cancelCh
+ d.cancelLock.RUnlock()
+ if cancel == nil {
+ return errNoSyncActive
+ }
+ select {
+ case destCh <- packet:
+ return nil
+ case <-cancel:
+ return errNoSyncActive
+ }
+}
diff --git a/les/downloader/downloader_test.go b/les/downloader/downloader_test.go
new file mode 100644
index 0000000000..17cd3630c9
--- /dev/null
+++ b/les/downloader/downloader_test.go
@@ -0,0 +1,1622 @@
+// 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 downloader
+
+import (
+ "errors"
+ "fmt"
+ "math/big"
+ "strings"
+ "sync"
+ "sync/atomic"
+ "testing"
+ "time"
+
+ "github.com/ethereum/go-ethereum"
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/state/snapshot"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/eth/protocols/eth"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/trie"
+)
+
+// Reduce some of the parameters to make the tester faster.
+func init() {
+ fullMaxForkAncestry = 10000
+ lightMaxForkAncestry = 10000
+ blockCacheMaxItems = 1024
+ fsHeaderContCheck = 500 * time.Millisecond
+}
+
+// downloadTester is a test simulator for mocking out local block chain.
+type downloadTester struct {
+ downloader *Downloader
+
+ genesis *types.Block // Genesis blocks used by the tester and peers
+ stateDb ethdb.Database // Database used by the tester for syncing from peers
+ peerDb ethdb.Database // Database of the peers containing all data
+ peers map[string]*downloadTesterPeer
+
+ ownHashes []common.Hash // Hash chain belonging to the tester
+ ownHeaders map[common.Hash]*types.Header // Headers belonging to the tester
+ ownBlocks map[common.Hash]*types.Block // Blocks belonging to the tester
+ ownReceipts map[common.Hash]types.Receipts // Receipts belonging to the tester
+ ownChainTd map[common.Hash]*big.Int // Total difficulties of the blocks in the local chain
+
+ ancientHeaders map[common.Hash]*types.Header // Ancient headers belonging to the tester
+ ancientBlocks map[common.Hash]*types.Block // Ancient blocks belonging to the tester
+ ancientReceipts map[common.Hash]types.Receipts // Ancient receipts belonging to the tester
+ ancientChainTd map[common.Hash]*big.Int // Ancient total difficulties of the blocks in the local chain
+
+ lock sync.RWMutex
+}
+
+// newTester creates a new downloader test mocker.
+func newTester() *downloadTester {
+ tester := &downloadTester{
+ genesis: testGenesis,
+ peerDb: testDB,
+ peers: make(map[string]*downloadTesterPeer),
+ ownHashes: []common.Hash{testGenesis.Hash()},
+ ownHeaders: map[common.Hash]*types.Header{testGenesis.Hash(): testGenesis.Header()},
+ ownBlocks: map[common.Hash]*types.Block{testGenesis.Hash(): testGenesis},
+ ownReceipts: map[common.Hash]types.Receipts{testGenesis.Hash(): nil},
+ ownChainTd: map[common.Hash]*big.Int{testGenesis.Hash(): testGenesis.Difficulty()},
+
+ // Initialize ancient store with test genesis block
+ ancientHeaders: map[common.Hash]*types.Header{testGenesis.Hash(): testGenesis.Header()},
+ ancientBlocks: map[common.Hash]*types.Block{testGenesis.Hash(): testGenesis},
+ ancientReceipts: map[common.Hash]types.Receipts{testGenesis.Hash(): nil},
+ ancientChainTd: map[common.Hash]*big.Int{testGenesis.Hash(): testGenesis.Difficulty()},
+ }
+ tester.stateDb = rawdb.NewMemoryDatabase()
+ tester.stateDb.Put(testGenesis.Root().Bytes(), []byte{0x00})
+
+ tester.downloader = New(0, tester.stateDb, trie.NewSyncBloom(1, tester.stateDb), new(event.TypeMux), tester, nil, tester.dropPeer)
+ return tester
+}
+
+// terminate aborts any operations on the embedded downloader and releases all
+// held resources.
+func (dl *downloadTester) terminate() {
+ dl.downloader.Terminate()
+}
+
+// sync starts synchronizing with a remote peer, blocking until it completes.
+func (dl *downloadTester) sync(id string, td *big.Int, mode SyncMode) error {
+ dl.lock.RLock()
+ hash := dl.peers[id].chain.headBlock().Hash()
+ // If no particular TD was requested, load from the peer's blockchain
+ if td == nil {
+ td = dl.peers[id].chain.td(hash)
+ }
+ dl.lock.RUnlock()
+
+ // Synchronise with the chosen peer and ensure proper cleanup afterwards
+ err := dl.downloader.synchronise(id, hash, td, mode)
+ select {
+ case <-dl.downloader.cancelCh:
+ // Ok, downloader fully cancelled after sync cycle
+ default:
+ // Downloader is still accepting packets, can block a peer up
+ panic("downloader active post sync cycle") // panic will be caught by tester
+ }
+ return err
+}
+
+// HasHeader checks if a header is present in the testers canonical chain.
+func (dl *downloadTester) HasHeader(hash common.Hash, number uint64) bool {
+ return dl.GetHeaderByHash(hash) != nil
+}
+
+// HasBlock checks if a block is present in the testers canonical chain.
+func (dl *downloadTester) HasBlock(hash common.Hash, number uint64) bool {
+ return dl.GetBlockByHash(hash) != nil
+}
+
+// HasFastBlock checks if a block is present in the testers canonical chain.
+func (dl *downloadTester) HasFastBlock(hash common.Hash, number uint64) bool {
+ dl.lock.RLock()
+ defer dl.lock.RUnlock()
+
+ if _, ok := dl.ancientReceipts[hash]; ok {
+ return true
+ }
+ _, ok := dl.ownReceipts[hash]
+ return ok
+}
+
+// GetHeader retrieves a header from the testers canonical chain.
+func (dl *downloadTester) GetHeaderByHash(hash common.Hash) *types.Header {
+ dl.lock.RLock()
+ defer dl.lock.RUnlock()
+ return dl.getHeaderByHash(hash)
+}
+
+// getHeaderByHash returns the header if found either within ancients or own blocks)
+// This method assumes that the caller holds at least the read-lock (dl.lock)
+func (dl *downloadTester) getHeaderByHash(hash common.Hash) *types.Header {
+ header := dl.ancientHeaders[hash]
+ if header != nil {
+ return header
+ }
+ return dl.ownHeaders[hash]
+}
+
+// GetBlock retrieves a block from the testers canonical chain.
+func (dl *downloadTester) GetBlockByHash(hash common.Hash) *types.Block {
+ dl.lock.RLock()
+ defer dl.lock.RUnlock()
+
+ block := dl.ancientBlocks[hash]
+ if block != nil {
+ return block
+ }
+ return dl.ownBlocks[hash]
+}
+
+// CurrentHeader retrieves the current head header from the canonical chain.
+func (dl *downloadTester) CurrentHeader() *types.Header {
+ dl.lock.RLock()
+ defer dl.lock.RUnlock()
+
+ for i := len(dl.ownHashes) - 1; i >= 0; i-- {
+ if header := dl.ancientHeaders[dl.ownHashes[i]]; header != nil {
+ return header
+ }
+ if header := dl.ownHeaders[dl.ownHashes[i]]; header != nil {
+ return header
+ }
+ }
+ return dl.genesis.Header()
+}
+
+// CurrentBlock retrieves the current head block from the canonical chain.
+func (dl *downloadTester) CurrentBlock() *types.Block {
+ dl.lock.RLock()
+ defer dl.lock.RUnlock()
+
+ for i := len(dl.ownHashes) - 1; i >= 0; i-- {
+ if block := dl.ancientBlocks[dl.ownHashes[i]]; block != nil {
+ if _, err := dl.stateDb.Get(block.Root().Bytes()); err == nil {
+ return block
+ }
+ return block
+ }
+ if block := dl.ownBlocks[dl.ownHashes[i]]; block != nil {
+ if _, err := dl.stateDb.Get(block.Root().Bytes()); err == nil {
+ return block
+ }
+ }
+ }
+ return dl.genesis
+}
+
+// CurrentFastBlock retrieves the current head fast-sync block from the canonical chain.
+func (dl *downloadTester) CurrentFastBlock() *types.Block {
+ dl.lock.RLock()
+ defer dl.lock.RUnlock()
+
+ for i := len(dl.ownHashes) - 1; i >= 0; i-- {
+ if block := dl.ancientBlocks[dl.ownHashes[i]]; block != nil {
+ return block
+ }
+ if block := dl.ownBlocks[dl.ownHashes[i]]; block != nil {
+ return block
+ }
+ }
+ return dl.genesis
+}
+
+// FastSyncCommitHead manually sets the head block to a given hash.
+func (dl *downloadTester) FastSyncCommitHead(hash common.Hash) error {
+ // For now only check that the state trie is correct
+ if block := dl.GetBlockByHash(hash); block != nil {
+ _, err := trie.NewSecure(block.Root(), trie.NewDatabase(dl.stateDb))
+ return err
+ }
+ return fmt.Errorf("non existent block: %x", hash[:4])
+}
+
+// GetTd retrieves the block's total difficulty from the canonical chain.
+func (dl *downloadTester) GetTd(hash common.Hash, number uint64) *big.Int {
+ dl.lock.RLock()
+ defer dl.lock.RUnlock()
+
+ return dl.getTd(hash)
+}
+
+// getTd retrieves the block's total difficulty if found either within
+// ancients or own blocks).
+// This method assumes that the caller holds at least the read-lock (dl.lock)
+func (dl *downloadTester) getTd(hash common.Hash) *big.Int {
+ if td := dl.ancientChainTd[hash]; td != nil {
+ return td
+ }
+ return dl.ownChainTd[hash]
+}
+
+// InsertHeaderChain injects a new batch of headers into the simulated chain.
+func (dl *downloadTester) InsertHeaderChain(headers []*types.Header, checkFreq int) (i int, err error) {
+ dl.lock.Lock()
+ defer dl.lock.Unlock()
+ // Do a quick check, as the blockchain.InsertHeaderChain doesn't insert anything in case of errors
+ if dl.getHeaderByHash(headers[0].ParentHash) == nil {
+ return 0, fmt.Errorf("InsertHeaderChain: unknown parent at first position, parent of number %d", headers[0].Number)
+ }
+ var hashes []common.Hash
+ for i := 1; i < len(headers); i++ {
+ hash := headers[i-1].Hash()
+ if headers[i].ParentHash != headers[i-1].Hash() {
+ return i, fmt.Errorf("non-contiguous import at position %d", i)
+ }
+ hashes = append(hashes, hash)
+ }
+ hashes = append(hashes, headers[len(headers)-1].Hash())
+ // Do a full insert if pre-checks passed
+ for i, header := range headers {
+ hash := hashes[i]
+ if dl.getHeaderByHash(hash) != nil {
+ continue
+ }
+ if dl.getHeaderByHash(header.ParentHash) == nil {
+ // This _should_ be impossible, due to precheck and induction
+ return i, fmt.Errorf("InsertHeaderChain: unknown parent at position %d", i)
+ }
+ dl.ownHashes = append(dl.ownHashes, hash)
+ dl.ownHeaders[hash] = header
+
+ td := dl.getTd(header.ParentHash)
+ dl.ownChainTd[hash] = new(big.Int).Add(td, header.Difficulty)
+ }
+ return len(headers), nil
+}
+
+// InsertChain injects a new batch of blocks into the simulated chain.
+func (dl *downloadTester) InsertChain(blocks types.Blocks) (i int, err error) {
+ dl.lock.Lock()
+ defer dl.lock.Unlock()
+ for i, block := range blocks {
+ if parent, ok := dl.ownBlocks[block.ParentHash()]; !ok {
+ return i, fmt.Errorf("InsertChain: unknown parent at position %d / %d", i, len(blocks))
+ } else if _, err := dl.stateDb.Get(parent.Root().Bytes()); err != nil {
+ return i, fmt.Errorf("InsertChain: unknown parent state %x: %v", parent.Root(), err)
+ }
+ if hdr := dl.getHeaderByHash(block.Hash()); hdr == nil {
+ dl.ownHashes = append(dl.ownHashes, block.Hash())
+ dl.ownHeaders[block.Hash()] = block.Header()
+ }
+ dl.ownBlocks[block.Hash()] = block
+ dl.ownReceipts[block.Hash()] = make(types.Receipts, 0)
+ dl.stateDb.Put(block.Root().Bytes(), []byte{0x00})
+ td := dl.getTd(block.ParentHash())
+ dl.ownChainTd[block.Hash()] = new(big.Int).Add(td, block.Difficulty())
+ }
+ return len(blocks), nil
+}
+
+// InsertReceiptChain injects a new batch of receipts into the simulated chain.
+func (dl *downloadTester) InsertReceiptChain(blocks types.Blocks, receipts []types.Receipts, ancientLimit uint64) (i int, err error) {
+ dl.lock.Lock()
+ defer dl.lock.Unlock()
+
+ for i := 0; i < len(blocks) && i < len(receipts); i++ {
+ if _, ok := dl.ownHeaders[blocks[i].Hash()]; !ok {
+ return i, errors.New("unknown owner")
+ }
+ if _, ok := dl.ancientBlocks[blocks[i].ParentHash()]; !ok {
+ if _, ok := dl.ownBlocks[blocks[i].ParentHash()]; !ok {
+ return i, errors.New("InsertReceiptChain: unknown parent")
+ }
+ }
+ if blocks[i].NumberU64() <= ancientLimit {
+ dl.ancientBlocks[blocks[i].Hash()] = blocks[i]
+ dl.ancientReceipts[blocks[i].Hash()] = receipts[i]
+
+ // Migrate from active db to ancient db
+ dl.ancientHeaders[blocks[i].Hash()] = blocks[i].Header()
+ dl.ancientChainTd[blocks[i].Hash()] = new(big.Int).Add(dl.ancientChainTd[blocks[i].ParentHash()], blocks[i].Difficulty())
+ delete(dl.ownHeaders, blocks[i].Hash())
+ delete(dl.ownChainTd, blocks[i].Hash())
+ } else {
+ dl.ownBlocks[blocks[i].Hash()] = blocks[i]
+ dl.ownReceipts[blocks[i].Hash()] = receipts[i]
+ }
+ }
+ return len(blocks), nil
+}
+
+// SetHead rewinds the local chain to a new head.
+func (dl *downloadTester) SetHead(head uint64) error {
+ dl.lock.Lock()
+ defer dl.lock.Unlock()
+
+ // Find the hash of the head to reset to
+ var hash common.Hash
+ for h, header := range dl.ownHeaders {
+ if header.Number.Uint64() == head {
+ hash = h
+ }
+ }
+ for h, header := range dl.ancientHeaders {
+ if header.Number.Uint64() == head {
+ hash = h
+ }
+ }
+ if hash == (common.Hash{}) {
+ return fmt.Errorf("unknown head to set: %d", head)
+ }
+ // Find the offset in the header chain
+ var offset int
+ for o, h := range dl.ownHashes {
+ if h == hash {
+ offset = o
+ break
+ }
+ }
+ // Remove all the hashes and associated data afterwards
+ for i := offset + 1; i < len(dl.ownHashes); i++ {
+ delete(dl.ownChainTd, dl.ownHashes[i])
+ delete(dl.ownHeaders, dl.ownHashes[i])
+ delete(dl.ownReceipts, dl.ownHashes[i])
+ delete(dl.ownBlocks, dl.ownHashes[i])
+
+ delete(dl.ancientChainTd, dl.ownHashes[i])
+ delete(dl.ancientHeaders, dl.ownHashes[i])
+ delete(dl.ancientReceipts, dl.ownHashes[i])
+ delete(dl.ancientBlocks, dl.ownHashes[i])
+ }
+ dl.ownHashes = dl.ownHashes[:offset+1]
+ return nil
+}
+
+// Rollback removes some recently added elements from the chain.
+func (dl *downloadTester) Rollback(hashes []common.Hash) {
+}
+
+// newPeer registers a new block download source into the downloader.
+func (dl *downloadTester) newPeer(id string, version uint, chain *testChain) error {
+ dl.lock.Lock()
+ defer dl.lock.Unlock()
+
+ peer := &downloadTesterPeer{dl: dl, id: id, chain: chain}
+ dl.peers[id] = peer
+ return dl.downloader.RegisterPeer(id, version, peer)
+}
+
+// dropPeer simulates a hard peer removal from the connection pool.
+func (dl *downloadTester) dropPeer(id string) {
+ dl.lock.Lock()
+ defer dl.lock.Unlock()
+
+ delete(dl.peers, id)
+ dl.downloader.UnregisterPeer(id)
+}
+
+// Snapshots implements the BlockChain interface for the downloader, but is a noop.
+func (dl *downloadTester) Snapshots() *snapshot.Tree {
+ return nil
+}
+
+type downloadTesterPeer struct {
+ dl *downloadTester
+ id string
+ chain *testChain
+ missingStates map[common.Hash]bool // State entries that fast sync should not return
+}
+
+// Head constructs a function to retrieve a peer's current head hash
+// and total difficulty.
+func (dlp *downloadTesterPeer) Head() (common.Hash, *big.Int) {
+ b := dlp.chain.headBlock()
+ return b.Hash(), dlp.chain.td(b.Hash())
+}
+
+// RequestHeadersByHash constructs a GetBlockHeaders function based on a hashed
+// origin; associated with a particular peer in the download tester. The returned
+// function can be used to retrieve batches of headers from the particular peer.
+func (dlp *downloadTesterPeer) RequestHeadersByHash(origin common.Hash, amount int, skip int, reverse bool) error {
+ result := dlp.chain.headersByHash(origin, amount, skip, reverse)
+ go dlp.dl.downloader.DeliverHeaders(dlp.id, result)
+ return nil
+}
+
+// RequestHeadersByNumber constructs a GetBlockHeaders function based on a numbered
+// origin; associated with a particular peer in the download tester. The returned
+// function can be used to retrieve batches of headers from the particular peer.
+func (dlp *downloadTesterPeer) RequestHeadersByNumber(origin uint64, amount int, skip int, reverse bool) error {
+ result := dlp.chain.headersByNumber(origin, amount, skip, reverse)
+ go dlp.dl.downloader.DeliverHeaders(dlp.id, result)
+ return nil
+}
+
+// RequestBodies constructs a getBlockBodies method associated with a particular
+// peer in the download tester. The returned function can be used to retrieve
+// batches of block bodies from the particularly requested peer.
+func (dlp *downloadTesterPeer) RequestBodies(hashes []common.Hash) error {
+ txs, uncles := dlp.chain.bodies(hashes)
+ go dlp.dl.downloader.DeliverBodies(dlp.id, txs, uncles)
+ return nil
+}
+
+// RequestReceipts constructs a getReceipts method associated with a particular
+// peer in the download tester. The returned function can be used to retrieve
+// batches of block receipts from the particularly requested peer.
+func (dlp *downloadTesterPeer) RequestReceipts(hashes []common.Hash) error {
+ receipts := dlp.chain.receipts(hashes)
+ go dlp.dl.downloader.DeliverReceipts(dlp.id, receipts)
+ return nil
+}
+
+// RequestNodeData constructs a getNodeData method associated with a particular
+// peer in the download tester. The returned function can be used to retrieve
+// batches of node state data from the particularly requested peer.
+func (dlp *downloadTesterPeer) RequestNodeData(hashes []common.Hash) error {
+ dlp.dl.lock.RLock()
+ defer dlp.dl.lock.RUnlock()
+
+ results := make([][]byte, 0, len(hashes))
+ for _, hash := range hashes {
+ if data, err := dlp.dl.peerDb.Get(hash.Bytes()); err == nil {
+ if !dlp.missingStates[hash] {
+ results = append(results, data)
+ }
+ }
+ }
+ go dlp.dl.downloader.DeliverNodeData(dlp.id, results)
+ return nil
+}
+
+// assertOwnChain checks if the local chain contains the correct number of items
+// of the various chain components.
+func assertOwnChain(t *testing.T, tester *downloadTester, length int) {
+ // Mark this method as a helper to report errors at callsite, not in here
+ t.Helper()
+
+ assertOwnForkedChain(t, tester, 1, []int{length})
+}
+
+// assertOwnForkedChain checks if the local forked chain contains the correct
+// number of items of the various chain components.
+func assertOwnForkedChain(t *testing.T, tester *downloadTester, common int, lengths []int) {
+ // Mark this method as a helper to report errors at callsite, not in here
+ t.Helper()
+
+ // Initialize the counters for the first fork
+ headers, blocks, receipts := lengths[0], lengths[0], lengths[0]
+
+ // Update the counters for each subsequent fork
+ for _, length := range lengths[1:] {
+ headers += length - common
+ blocks += length - common
+ receipts += length - common
+ }
+ if tester.downloader.getMode() == LightSync {
+ blocks, receipts = 1, 1
+ }
+ if hs := len(tester.ownHeaders) + len(tester.ancientHeaders) - 1; hs != headers {
+ t.Fatalf("synchronised headers mismatch: have %v, want %v", hs, headers)
+ }
+ if bs := len(tester.ownBlocks) + len(tester.ancientBlocks) - 1; bs != blocks {
+ t.Fatalf("synchronised blocks mismatch: have %v, want %v", bs, blocks)
+ }
+ if rs := len(tester.ownReceipts) + len(tester.ancientReceipts) - 1; rs != receipts {
+ t.Fatalf("synchronised receipts mismatch: have %v, want %v", rs, receipts)
+ }
+}
+
+func TestCanonicalSynchronisation66Full(t *testing.T) { testCanonSync(t, eth.ETH66, FullSync) }
+func TestCanonicalSynchronisation66Fast(t *testing.T) { testCanonSync(t, eth.ETH66, FastSync) }
+func TestCanonicalSynchronisation66Light(t *testing.T) { testCanonSync(t, eth.ETH66, LightSync) }
+
+func testCanonSync(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ tester := newTester()
+ defer tester.terminate()
+
+ // Create a small enough block chain to download
+ chain := testChainBase.shorten(blockCacheMaxItems - 15)
+ tester.newPeer("peer", protocol, chain)
+
+ // Synchronise with the peer and make sure all relevant data was retrieved
+ if err := tester.sync("peer", nil, mode); err != nil {
+ t.Fatalf("failed to synchronise blocks: %v", err)
+ }
+ assertOwnChain(t, tester, chain.len())
+}
+
+// Tests that if a large batch of blocks are being downloaded, it is throttled
+// until the cached blocks are retrieved.
+func TestThrottling66Full(t *testing.T) { testThrottling(t, eth.ETH66, FullSync) }
+func TestThrottling66Fast(t *testing.T) { testThrottling(t, eth.ETH66, FastSync) }
+
+func testThrottling(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+ tester := newTester()
+
+ // Create a long block chain to download and the tester
+ targetBlocks := testChainBase.len() - 1
+ tester.newPeer("peer", protocol, testChainBase)
+
+ // Wrap the importer to allow stepping
+ blocked, proceed := uint32(0), make(chan struct{})
+ tester.downloader.chainInsertHook = func(results []*fetchResult) {
+ atomic.StoreUint32(&blocked, uint32(len(results)))
+ <-proceed
+ }
+ // Start a synchronisation concurrently
+ errc := make(chan error, 1)
+ go func() {
+ errc <- tester.sync("peer", nil, mode)
+ }()
+ // Iteratively take some blocks, always checking the retrieval count
+ for {
+ // Check the retrieval count synchronously (! reason for this ugly block)
+ tester.lock.RLock()
+ retrieved := len(tester.ownBlocks)
+ tester.lock.RUnlock()
+ if retrieved >= targetBlocks+1 {
+ break
+ }
+ // Wait a bit for sync to throttle itself
+ var cached, frozen int
+ for start := time.Now(); time.Since(start) < 3*time.Second; {
+ time.Sleep(25 * time.Millisecond)
+
+ tester.lock.Lock()
+ tester.downloader.queue.lock.Lock()
+ tester.downloader.queue.resultCache.lock.Lock()
+ {
+ cached = tester.downloader.queue.resultCache.countCompleted()
+ frozen = int(atomic.LoadUint32(&blocked))
+ retrieved = len(tester.ownBlocks)
+ }
+ tester.downloader.queue.resultCache.lock.Unlock()
+ tester.downloader.queue.lock.Unlock()
+ tester.lock.Unlock()
+
+ if cached == blockCacheMaxItems ||
+ cached == blockCacheMaxItems-reorgProtHeaderDelay ||
+ retrieved+cached+frozen == targetBlocks+1 ||
+ retrieved+cached+frozen == targetBlocks+1-reorgProtHeaderDelay {
+ break
+ }
+ }
+ // Make sure we filled up the cache, then exhaust it
+ time.Sleep(25 * time.Millisecond) // give it a chance to screw up
+ tester.lock.RLock()
+ retrieved = len(tester.ownBlocks)
+ tester.lock.RUnlock()
+ if cached != blockCacheMaxItems && cached != blockCacheMaxItems-reorgProtHeaderDelay && retrieved+cached+frozen != targetBlocks+1 && retrieved+cached+frozen != targetBlocks+1-reorgProtHeaderDelay {
+ t.Fatalf("block count mismatch: have %v, want %v (owned %v, blocked %v, target %v)", cached, blockCacheMaxItems, retrieved, frozen, targetBlocks+1)
+ }
+
+ // Permit the blocked blocks to import
+ if atomic.LoadUint32(&blocked) > 0 {
+ atomic.StoreUint32(&blocked, uint32(0))
+ proceed <- struct{}{}
+ }
+ }
+ // Check that we haven't pulled more blocks than available
+ assertOwnChain(t, tester, targetBlocks+1)
+ if err := <-errc; err != nil {
+ t.Fatalf("block synchronization failed: %v", err)
+ }
+ tester.terminate()
+
+}
+
+// Tests that simple synchronization against a forked chain works correctly. In
+// this test common ancestor lookup should *not* be short circuited, and a full
+// binary search should be executed.
+func TestForkedSync66Full(t *testing.T) { testForkedSync(t, eth.ETH66, FullSync) }
+func TestForkedSync66Fast(t *testing.T) { testForkedSync(t, eth.ETH66, FastSync) }
+func TestForkedSync66Light(t *testing.T) { testForkedSync(t, eth.ETH66, LightSync) }
+
+func testForkedSync(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ tester := newTester()
+ defer tester.terminate()
+
+ chainA := testChainForkLightA.shorten(testChainBase.len() + 80)
+ chainB := testChainForkLightB.shorten(testChainBase.len() + 80)
+ tester.newPeer("fork A", protocol, chainA)
+ tester.newPeer("fork B", protocol, chainB)
+ // Synchronise with the peer and make sure all blocks were retrieved
+ if err := tester.sync("fork A", nil, mode); err != nil {
+ t.Fatalf("failed to synchronise blocks: %v", err)
+ }
+ assertOwnChain(t, tester, chainA.len())
+
+ // Synchronise with the second peer and make sure that fork is pulled too
+ if err := tester.sync("fork B", nil, mode); err != nil {
+ t.Fatalf("failed to synchronise blocks: %v", err)
+ }
+ assertOwnForkedChain(t, tester, testChainBase.len(), []int{chainA.len(), chainB.len()})
+}
+
+// Tests that synchronising against a much shorter but much heavyer fork works
+// corrently and is not dropped.
+func TestHeavyForkedSync66Full(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, FullSync) }
+func TestHeavyForkedSync66Fast(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, FastSync) }
+func TestHeavyForkedSync66Light(t *testing.T) { testHeavyForkedSync(t, eth.ETH66, LightSync) }
+
+func testHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ tester := newTester()
+ defer tester.terminate()
+
+ chainA := testChainForkLightA.shorten(testChainBase.len() + 80)
+ chainB := testChainForkHeavy.shorten(testChainBase.len() + 80)
+ tester.newPeer("light", protocol, chainA)
+ tester.newPeer("heavy", protocol, chainB)
+
+ // Synchronise with the peer and make sure all blocks were retrieved
+ if err := tester.sync("light", nil, mode); err != nil {
+ t.Fatalf("failed to synchronise blocks: %v", err)
+ }
+ assertOwnChain(t, tester, chainA.len())
+
+ // Synchronise with the second peer and make sure that fork is pulled too
+ if err := tester.sync("heavy", nil, mode); err != nil {
+ t.Fatalf("failed to synchronise blocks: %v", err)
+ }
+ assertOwnForkedChain(t, tester, testChainBase.len(), []int{chainA.len(), chainB.len()})
+}
+
+// Tests that chain forks are contained within a certain interval of the current
+// chain head, ensuring that malicious peers cannot waste resources by feeding
+// long dead chains.
+func TestBoundedForkedSync66Full(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, FullSync) }
+func TestBoundedForkedSync66Fast(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, FastSync) }
+func TestBoundedForkedSync66Light(t *testing.T) { testBoundedForkedSync(t, eth.ETH66, LightSync) }
+
+func testBoundedForkedSync(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ tester := newTester()
+ defer tester.terminate()
+
+ chainA := testChainForkLightA
+ chainB := testChainForkLightB
+ tester.newPeer("original", protocol, chainA)
+ tester.newPeer("rewriter", protocol, chainB)
+
+ // Synchronise with the peer and make sure all blocks were retrieved
+ if err := tester.sync("original", nil, mode); err != nil {
+ t.Fatalf("failed to synchronise blocks: %v", err)
+ }
+ assertOwnChain(t, tester, chainA.len())
+
+ // Synchronise with the second peer and ensure that the fork is rejected to being too old
+ if err := tester.sync("rewriter", nil, mode); err != errInvalidAncestor {
+ t.Fatalf("sync failure mismatch: have %v, want %v", err, errInvalidAncestor)
+ }
+}
+
+// Tests that chain forks are contained within a certain interval of the current
+// chain head for short but heavy forks too. These are a bit special because they
+// take different ancestor lookup paths.
+func TestBoundedHeavyForkedSync66Full(t *testing.T) {
+ testBoundedHeavyForkedSync(t, eth.ETH66, FullSync)
+}
+func TestBoundedHeavyForkedSync66Fast(t *testing.T) {
+ testBoundedHeavyForkedSync(t, eth.ETH66, FastSync)
+}
+func TestBoundedHeavyForkedSync66Light(t *testing.T) {
+ testBoundedHeavyForkedSync(t, eth.ETH66, LightSync)
+}
+
+func testBoundedHeavyForkedSync(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+ tester := newTester()
+
+ // Create a long enough forked chain
+ chainA := testChainForkLightA
+ chainB := testChainForkHeavy
+ tester.newPeer("original", protocol, chainA)
+
+ // Synchronise with the peer and make sure all blocks were retrieved
+ if err := tester.sync("original", nil, mode); err != nil {
+ t.Fatalf("failed to synchronise blocks: %v", err)
+ }
+ assertOwnChain(t, tester, chainA.len())
+
+ tester.newPeer("heavy-rewriter", protocol, chainB)
+ // Synchronise with the second peer and ensure that the fork is rejected to being too old
+ if err := tester.sync("heavy-rewriter", nil, mode); err != errInvalidAncestor {
+ t.Fatalf("sync failure mismatch: have %v, want %v", err, errInvalidAncestor)
+ }
+ tester.terminate()
+}
+
+// Tests that an inactive downloader will not accept incoming block headers,
+// bodies and receipts.
+func TestInactiveDownloader63(t *testing.T) {
+ t.Parallel()
+
+ tester := newTester()
+ defer tester.terminate()
+
+ // Check that neither block headers nor bodies are accepted
+ if err := tester.downloader.DeliverHeaders("bad peer", []*types.Header{}); err != errNoSyncActive {
+ t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive)
+ }
+ if err := tester.downloader.DeliverBodies("bad peer", [][]*types.Transaction{}, [][]*types.Header{}); err != errNoSyncActive {
+ t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive)
+ }
+ if err := tester.downloader.DeliverReceipts("bad peer", [][]*types.Receipt{}); err != errNoSyncActive {
+ t.Errorf("error mismatch: have %v, want %v", err, errNoSyncActive)
+ }
+}
+
+// Tests that a canceled download wipes all previously accumulated state.
+func TestCancel66Full(t *testing.T) { testCancel(t, eth.ETH66, FullSync) }
+func TestCancel66Fast(t *testing.T) { testCancel(t, eth.ETH66, FastSync) }
+func TestCancel66Light(t *testing.T) { testCancel(t, eth.ETH66, LightSync) }
+
+func testCancel(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ tester := newTester()
+ defer tester.terminate()
+
+ chain := testChainBase.shorten(MaxHeaderFetch)
+ tester.newPeer("peer", protocol, chain)
+
+ // Make sure canceling works with a pristine downloader
+ tester.downloader.Cancel()
+ if !tester.downloader.queue.Idle() {
+ t.Errorf("download queue not idle")
+ }
+ // Synchronise with the peer, but cancel afterwards
+ if err := tester.sync("peer", nil, mode); err != nil {
+ t.Fatalf("failed to synchronise blocks: %v", err)
+ }
+ tester.downloader.Cancel()
+ if !tester.downloader.queue.Idle() {
+ t.Errorf("download queue not idle")
+ }
+}
+
+// Tests that synchronisation from multiple peers works as intended (multi thread sanity test).
+func TestMultiSynchronisation66Full(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, FullSync) }
+func TestMultiSynchronisation66Fast(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, FastSync) }
+func TestMultiSynchronisation66Light(t *testing.T) { testMultiSynchronisation(t, eth.ETH66, LightSync) }
+
+func testMultiSynchronisation(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ tester := newTester()
+ defer tester.terminate()
+
+ // Create various peers with various parts of the chain
+ targetPeers := 8
+ chain := testChainBase.shorten(targetPeers * 100)
+
+ for i := 0; i < targetPeers; i++ {
+ id := fmt.Sprintf("peer #%d", i)
+ tester.newPeer(id, protocol, chain.shorten(chain.len()/(i+1)))
+ }
+ if err := tester.sync("peer #0", nil, mode); err != nil {
+ t.Fatalf("failed to synchronise blocks: %v", err)
+ }
+ assertOwnChain(t, tester, chain.len())
+}
+
+// Tests that synchronisations behave well in multi-version protocol environments
+// and not wreak havoc on other nodes in the network.
+func TestMultiProtoSynchronisation66Full(t *testing.T) { testMultiProtoSync(t, eth.ETH66, FullSync) }
+func TestMultiProtoSynchronisation66Fast(t *testing.T) { testMultiProtoSync(t, eth.ETH66, FastSync) }
+func TestMultiProtoSynchronisation66Light(t *testing.T) { testMultiProtoSync(t, eth.ETH66, LightSync) }
+
+func testMultiProtoSync(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ tester := newTester()
+ defer tester.terminate()
+
+ // Create a small enough block chain to download
+ chain := testChainBase.shorten(blockCacheMaxItems - 15)
+
+ // Create peers of every type
+ tester.newPeer("peer 66", eth.ETH66, chain)
+ //tester.newPeer("peer 65", eth.ETH67, chain)
+
+ // Synchronise with the requested peer and make sure all blocks were retrieved
+ if err := tester.sync(fmt.Sprintf("peer %d", protocol), nil, mode); err != nil {
+ t.Fatalf("failed to synchronise blocks: %v", err)
+ }
+ assertOwnChain(t, tester, chain.len())
+
+ // Check that no peers have been dropped off
+ for _, version := range []int{66} {
+ peer := fmt.Sprintf("peer %d", version)
+ if _, ok := tester.peers[peer]; !ok {
+ t.Errorf("%s dropped", peer)
+ }
+ }
+}
+
+// Tests that if a block is empty (e.g. header only), no body request should be
+// made, and instead the header should be assembled into a whole block in itself.
+func TestEmptyShortCircuit66Full(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, FullSync) }
+func TestEmptyShortCircuit66Fast(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, FastSync) }
+func TestEmptyShortCircuit66Light(t *testing.T) { testEmptyShortCircuit(t, eth.ETH66, LightSync) }
+
+func testEmptyShortCircuit(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ tester := newTester()
+ defer tester.terminate()
+
+ // Create a block chain to download
+ chain := testChainBase
+ tester.newPeer("peer", protocol, chain)
+
+ // Instrument the downloader to signal body requests
+ bodiesHave, receiptsHave := int32(0), int32(0)
+ tester.downloader.bodyFetchHook = func(headers []*types.Header) {
+ atomic.AddInt32(&bodiesHave, int32(len(headers)))
+ }
+ tester.downloader.receiptFetchHook = func(headers []*types.Header) {
+ atomic.AddInt32(&receiptsHave, int32(len(headers)))
+ }
+ // Synchronise with the peer and make sure all blocks were retrieved
+ if err := tester.sync("peer", nil, mode); err != nil {
+ t.Fatalf("failed to synchronise blocks: %v", err)
+ }
+ assertOwnChain(t, tester, chain.len())
+
+ // Validate the number of block bodies that should have been requested
+ bodiesNeeded, receiptsNeeded := 0, 0
+ for _, block := range chain.blockm {
+ if mode != LightSync && block != tester.genesis && (len(block.Transactions()) > 0 || len(block.Uncles()) > 0) {
+ bodiesNeeded++
+ }
+ }
+ for _, receipt := range chain.receiptm {
+ if mode == FastSync && len(receipt) > 0 {
+ receiptsNeeded++
+ }
+ }
+ if int(bodiesHave) != bodiesNeeded {
+ t.Errorf("body retrieval count mismatch: have %v, want %v", bodiesHave, bodiesNeeded)
+ }
+ if int(receiptsHave) != receiptsNeeded {
+ t.Errorf("receipt retrieval count mismatch: have %v, want %v", receiptsHave, receiptsNeeded)
+ }
+}
+
+// Tests that headers are enqueued continuously, preventing malicious nodes from
+// stalling the downloader by feeding gapped header chains.
+func TestMissingHeaderAttack66Full(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, FullSync) }
+func TestMissingHeaderAttack66Fast(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, FastSync) }
+func TestMissingHeaderAttack66Light(t *testing.T) { testMissingHeaderAttack(t, eth.ETH66, LightSync) }
+
+func testMissingHeaderAttack(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ tester := newTester()
+ defer tester.terminate()
+
+ chain := testChainBase.shorten(blockCacheMaxItems - 15)
+ brokenChain := chain.shorten(chain.len())
+ delete(brokenChain.headerm, brokenChain.chain[brokenChain.len()/2])
+ tester.newPeer("attack", protocol, brokenChain)
+
+ if err := tester.sync("attack", nil, mode); err == nil {
+ t.Fatalf("succeeded attacker synchronisation")
+ }
+ // Synchronise with the valid peer and make sure sync succeeds
+ tester.newPeer("valid", protocol, chain)
+ if err := tester.sync("valid", nil, mode); err != nil {
+ t.Fatalf("failed to synchronise blocks: %v", err)
+ }
+ assertOwnChain(t, tester, chain.len())
+}
+
+// Tests that if requested headers are shifted (i.e. first is missing), the queue
+// detects the invalid numbering.
+func TestShiftedHeaderAttack66Full(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, FullSync) }
+func TestShiftedHeaderAttack66Fast(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, FastSync) }
+func TestShiftedHeaderAttack66Light(t *testing.T) { testShiftedHeaderAttack(t, eth.ETH66, LightSync) }
+
+func testShiftedHeaderAttack(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ tester := newTester()
+ defer tester.terminate()
+
+ chain := testChainBase.shorten(blockCacheMaxItems - 15)
+
+ // Attempt a full sync with an attacker feeding shifted headers
+ brokenChain := chain.shorten(chain.len())
+ delete(brokenChain.headerm, brokenChain.chain[1])
+ delete(brokenChain.blockm, brokenChain.chain[1])
+ delete(brokenChain.receiptm, brokenChain.chain[1])
+ tester.newPeer("attack", protocol, brokenChain)
+ if err := tester.sync("attack", nil, mode); err == nil {
+ t.Fatalf("succeeded attacker synchronisation")
+ }
+
+ // Synchronise with the valid peer and make sure sync succeeds
+ tester.newPeer("valid", protocol, chain)
+ if err := tester.sync("valid", nil, mode); err != nil {
+ t.Fatalf("failed to synchronise blocks: %v", err)
+ }
+ assertOwnChain(t, tester, chain.len())
+}
+
+// Tests that upon detecting an invalid header, the recent ones are rolled back
+// for various failure scenarios. Afterwards a full sync is attempted to make
+// sure no state was corrupted.
+func TestInvalidHeaderRollback66Fast(t *testing.T) { testInvalidHeaderRollback(t, eth.ETH66, FastSync) }
+
+func testInvalidHeaderRollback(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ tester := newTester()
+
+ // Create a small enough block chain to download
+ targetBlocks := 3*fsHeaderSafetyNet + 256 + fsMinFullBlocks
+ chain := testChainBase.shorten(targetBlocks)
+
+ // Attempt to sync with an attacker that feeds junk during the fast sync phase.
+ // This should result in the last fsHeaderSafetyNet headers being rolled back.
+ missing := fsHeaderSafetyNet + MaxHeaderFetch + 1
+ fastAttackChain := chain.shorten(chain.len())
+ delete(fastAttackChain.headerm, fastAttackChain.chain[missing])
+ tester.newPeer("fast-attack", protocol, fastAttackChain)
+
+ if err := tester.sync("fast-attack", nil, mode); err == nil {
+ t.Fatalf("succeeded fast attacker synchronisation")
+ }
+ if head := tester.CurrentHeader().Number.Int64(); int(head) > MaxHeaderFetch {
+ t.Errorf("rollback head mismatch: have %v, want at most %v", head, MaxHeaderFetch)
+ }
+
+ // Attempt to sync with an attacker that feeds junk during the block import phase.
+ // This should result in both the last fsHeaderSafetyNet number of headers being
+ // rolled back, and also the pivot point being reverted to a non-block status.
+ missing = 3*fsHeaderSafetyNet + MaxHeaderFetch + 1
+ blockAttackChain := chain.shorten(chain.len())
+ delete(fastAttackChain.headerm, fastAttackChain.chain[missing]) // Make sure the fast-attacker doesn't fill in
+ delete(blockAttackChain.headerm, blockAttackChain.chain[missing])
+ tester.newPeer("block-attack", protocol, blockAttackChain)
+
+ if err := tester.sync("block-attack", nil, mode); err == nil {
+ t.Fatalf("succeeded block attacker synchronisation")
+ }
+ if head := tester.CurrentHeader().Number.Int64(); int(head) > 2*fsHeaderSafetyNet+MaxHeaderFetch {
+ t.Errorf("rollback head mismatch: have %v, want at most %v", head, 2*fsHeaderSafetyNet+MaxHeaderFetch)
+ }
+ if mode == FastSync {
+ if head := tester.CurrentBlock().NumberU64(); head != 0 {
+ t.Errorf("fast sync pivot block #%d not rolled back", head)
+ }
+ }
+
+ // Attempt to sync with an attacker that withholds promised blocks after the
+ // fast sync pivot point. This could be a trial to leave the node with a bad
+ // but already imported pivot block.
+ withholdAttackChain := chain.shorten(chain.len())
+ tester.newPeer("withhold-attack", protocol, withholdAttackChain)
+ tester.downloader.syncInitHook = func(uint64, uint64) {
+ for i := missing; i < withholdAttackChain.len(); i++ {
+ delete(withholdAttackChain.headerm, withholdAttackChain.chain[i])
+ }
+ tester.downloader.syncInitHook = nil
+ }
+ if err := tester.sync("withhold-attack", nil, mode); err == nil {
+ t.Fatalf("succeeded withholding attacker synchronisation")
+ }
+ if head := tester.CurrentHeader().Number.Int64(); int(head) > 2*fsHeaderSafetyNet+MaxHeaderFetch {
+ t.Errorf("rollback head mismatch: have %v, want at most %v", head, 2*fsHeaderSafetyNet+MaxHeaderFetch)
+ }
+ if mode == FastSync {
+ if head := tester.CurrentBlock().NumberU64(); head != 0 {
+ t.Errorf("fast sync pivot block #%d not rolled back", head)
+ }
+ }
+
+ // synchronise with the valid peer and make sure sync succeeds. Since the last rollback
+ // should also disable fast syncing for this process, verify that we did a fresh full
+ // sync. Note, we can't assert anything about the receipts since we won't purge the
+ // database of them, hence we can't use assertOwnChain.
+ tester.newPeer("valid", protocol, chain)
+ if err := tester.sync("valid", nil, mode); err != nil {
+ t.Fatalf("failed to synchronise blocks: %v", err)
+ }
+ if hs := len(tester.ownHeaders); hs != chain.len() {
+ t.Fatalf("synchronised headers mismatch: have %v, want %v", hs, chain.len())
+ }
+ if mode != LightSync {
+ if bs := len(tester.ownBlocks); bs != chain.len() {
+ t.Fatalf("synchronised blocks mismatch: have %v, want %v", bs, chain.len())
+ }
+ }
+ tester.terminate()
+}
+
+// Tests that a peer advertising a high TD doesn't get to stall the downloader
+// afterwards by not sending any useful hashes.
+func TestHighTDStarvationAttack66Full(t *testing.T) {
+ testHighTDStarvationAttack(t, eth.ETH66, FullSync)
+}
+func TestHighTDStarvationAttack66Fast(t *testing.T) {
+ testHighTDStarvationAttack(t, eth.ETH66, FastSync)
+}
+func TestHighTDStarvationAttack66Light(t *testing.T) {
+ testHighTDStarvationAttack(t, eth.ETH66, LightSync)
+}
+
+func testHighTDStarvationAttack(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ tester := newTester()
+
+ chain := testChainBase.shorten(1)
+ tester.newPeer("attack", protocol, chain)
+ if err := tester.sync("attack", big.NewInt(1000000), mode); err != errStallingPeer {
+ t.Fatalf("synchronisation error mismatch: have %v, want %v", err, errStallingPeer)
+ }
+ tester.terminate()
+}
+
+// Tests that misbehaving peers are disconnected, whilst behaving ones are not.
+func TestBlockHeaderAttackerDropping66(t *testing.T) { testBlockHeaderAttackerDropping(t, eth.ETH66) }
+
+func testBlockHeaderAttackerDropping(t *testing.T, protocol uint) {
+ t.Parallel()
+
+ // Define the disconnection requirement for individual hash fetch errors
+ tests := []struct {
+ result error
+ drop bool
+ }{
+ {nil, false}, // Sync succeeded, all is well
+ {errBusy, false}, // Sync is already in progress, no problem
+ {errUnknownPeer, false}, // Peer is unknown, was already dropped, don't double drop
+ {errBadPeer, true}, // Peer was deemed bad for some reason, drop it
+ {errStallingPeer, true}, // Peer was detected to be stalling, drop it
+ {errUnsyncedPeer, true}, // Peer was detected to be unsynced, drop it
+ {errNoPeers, false}, // No peers to download from, soft race, no issue
+ {errTimeout, true}, // No hashes received in due time, drop the peer
+ {errEmptyHeaderSet, true}, // No headers were returned as a response, drop as it's a dead end
+ {errPeersUnavailable, true}, // Nobody had the advertised blocks, drop the advertiser
+ {errInvalidAncestor, true}, // Agreed upon ancestor is not acceptable, drop the chain rewriter
+ {errInvalidChain, true}, // Hash chain was detected as invalid, definitely drop
+ {errInvalidBody, false}, // A bad peer was detected, but not the sync origin
+ {errInvalidReceipt, false}, // A bad peer was detected, but not the sync origin
+ {errCancelContentProcessing, false}, // Synchronisation was canceled, origin may be innocent, don't drop
+ }
+ // Run the tests and check disconnection status
+ tester := newTester()
+ defer tester.terminate()
+ chain := testChainBase.shorten(1)
+
+ for i, tt := range tests {
+ // Register a new peer and ensure its presence
+ id := fmt.Sprintf("test %d", i)
+ if err := tester.newPeer(id, protocol, chain); err != nil {
+ t.Fatalf("test %d: failed to register new peer: %v", i, err)
+ }
+ if _, ok := tester.peers[id]; !ok {
+ t.Fatalf("test %d: registered peer not found", i)
+ }
+ // Simulate a synchronisation and check the required result
+ tester.downloader.synchroniseMock = func(string, common.Hash) error { return tt.result }
+
+ tester.downloader.Synchronise(id, tester.genesis.Hash(), big.NewInt(1000), FullSync)
+ if _, ok := tester.peers[id]; !ok != tt.drop {
+ t.Errorf("test %d: peer drop mismatch for %v: have %v, want %v", i, tt.result, !ok, tt.drop)
+ }
+ }
+}
+
+// Tests that synchronisation progress (origin block number, current block number
+// and highest block number) is tracked and updated correctly.
+func TestSyncProgress66Full(t *testing.T) { testSyncProgress(t, eth.ETH66, FullSync) }
+func TestSyncProgress66Fast(t *testing.T) { testSyncProgress(t, eth.ETH66, FastSync) }
+func TestSyncProgress66Light(t *testing.T) { testSyncProgress(t, eth.ETH66, LightSync) }
+
+func testSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ tester := newTester()
+ defer tester.terminate()
+ chain := testChainBase.shorten(blockCacheMaxItems - 15)
+
+ // Set a sync init hook to catch progress changes
+ starting := make(chan struct{})
+ progress := make(chan struct{})
+
+ tester.downloader.syncInitHook = func(origin, latest uint64) {
+ starting <- struct{}{}
+ <-progress
+ }
+ checkProgress(t, tester.downloader, "pristine", ethereum.SyncProgress{})
+
+ // Synchronise half the blocks and check initial progress
+ tester.newPeer("peer-half", protocol, chain.shorten(chain.len()/2))
+ pending := new(sync.WaitGroup)
+ pending.Add(1)
+
+ go func() {
+ defer pending.Done()
+ if err := tester.sync("peer-half", nil, mode); err != nil {
+ panic(fmt.Sprintf("failed to synchronise blocks: %v", err))
+ }
+ }()
+ <-starting
+ checkProgress(t, tester.downloader, "initial", ethereum.SyncProgress{
+ HighestBlock: uint64(chain.len()/2 - 1),
+ })
+ progress <- struct{}{}
+ pending.Wait()
+
+ // Synchronise all the blocks and check continuation progress
+ tester.newPeer("peer-full", protocol, chain)
+ pending.Add(1)
+ go func() {
+ defer pending.Done()
+ if err := tester.sync("peer-full", nil, mode); err != nil {
+ panic(fmt.Sprintf("failed to synchronise blocks: %v", err))
+ }
+ }()
+ <-starting
+ checkProgress(t, tester.downloader, "completing", ethereum.SyncProgress{
+ StartingBlock: uint64(chain.len()/2 - 1),
+ CurrentBlock: uint64(chain.len()/2 - 1),
+ HighestBlock: uint64(chain.len() - 1),
+ })
+
+ // Check final progress after successful sync
+ progress <- struct{}{}
+ pending.Wait()
+ checkProgress(t, tester.downloader, "final", ethereum.SyncProgress{
+ StartingBlock: uint64(chain.len()/2 - 1),
+ CurrentBlock: uint64(chain.len() - 1),
+ HighestBlock: uint64(chain.len() - 1),
+ })
+}
+
+func checkProgress(t *testing.T, d *Downloader, stage string, want ethereum.SyncProgress) {
+ // Mark this method as a helper to report errors at callsite, not in here
+ t.Helper()
+
+ p := d.Progress()
+ p.KnownStates, p.PulledStates = 0, 0
+ want.KnownStates, want.PulledStates = 0, 0
+ if p != want {
+ t.Fatalf("%s progress mismatch:\nhave %+v\nwant %+v", stage, p, want)
+ }
+}
+
+// Tests that synchronisation progress (origin block number and highest block
+// number) is tracked and updated correctly in case of a fork (or manual head
+// revertal).
+func TestForkedSyncProgress66Full(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, FullSync) }
+func TestForkedSyncProgress66Fast(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, FastSync) }
+func TestForkedSyncProgress66Light(t *testing.T) { testForkedSyncProgress(t, eth.ETH66, LightSync) }
+
+func testForkedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ tester := newTester()
+ defer tester.terminate()
+ chainA := testChainForkLightA.shorten(testChainBase.len() + MaxHeaderFetch)
+ chainB := testChainForkLightB.shorten(testChainBase.len() + MaxHeaderFetch)
+
+ // Set a sync init hook to catch progress changes
+ starting := make(chan struct{})
+ progress := make(chan struct{})
+
+ tester.downloader.syncInitHook = func(origin, latest uint64) {
+ starting <- struct{}{}
+ <-progress
+ }
+ checkProgress(t, tester.downloader, "pristine", ethereum.SyncProgress{})
+
+ // Synchronise with one of the forks and check progress
+ tester.newPeer("fork A", protocol, chainA)
+ pending := new(sync.WaitGroup)
+ pending.Add(1)
+ go func() {
+ defer pending.Done()
+ if err := tester.sync("fork A", nil, mode); err != nil {
+ panic(fmt.Sprintf("failed to synchronise blocks: %v", err))
+ }
+ }()
+ <-starting
+
+ checkProgress(t, tester.downloader, "initial", ethereum.SyncProgress{
+ HighestBlock: uint64(chainA.len() - 1),
+ })
+ progress <- struct{}{}
+ pending.Wait()
+
+ // Simulate a successful sync above the fork
+ tester.downloader.syncStatsChainOrigin = tester.downloader.syncStatsChainHeight
+
+ // Synchronise with the second fork and check progress resets
+ tester.newPeer("fork B", protocol, chainB)
+ pending.Add(1)
+ go func() {
+ defer pending.Done()
+ if err := tester.sync("fork B", nil, mode); err != nil {
+ panic(fmt.Sprintf("failed to synchronise blocks: %v", err))
+ }
+ }()
+ <-starting
+ checkProgress(t, tester.downloader, "forking", ethereum.SyncProgress{
+ StartingBlock: uint64(testChainBase.len()) - 1,
+ CurrentBlock: uint64(chainA.len() - 1),
+ HighestBlock: uint64(chainB.len() - 1),
+ })
+
+ // Check final progress after successful sync
+ progress <- struct{}{}
+ pending.Wait()
+ checkProgress(t, tester.downloader, "final", ethereum.SyncProgress{
+ StartingBlock: uint64(testChainBase.len()) - 1,
+ CurrentBlock: uint64(chainB.len() - 1),
+ HighestBlock: uint64(chainB.len() - 1),
+ })
+}
+
+// Tests that if synchronisation is aborted due to some failure, then the progress
+// origin is not updated in the next sync cycle, as it should be considered the
+// continuation of the previous sync and not a new instance.
+func TestFailedSyncProgress66Full(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, FullSync) }
+func TestFailedSyncProgress66Fast(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, FastSync) }
+func TestFailedSyncProgress66Light(t *testing.T) { testFailedSyncProgress(t, eth.ETH66, LightSync) }
+
+func testFailedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ tester := newTester()
+ defer tester.terminate()
+ chain := testChainBase.shorten(blockCacheMaxItems - 15)
+
+ // Set a sync init hook to catch progress changes
+ starting := make(chan struct{})
+ progress := make(chan struct{})
+
+ tester.downloader.syncInitHook = func(origin, latest uint64) {
+ starting <- struct{}{}
+ <-progress
+ }
+ checkProgress(t, tester.downloader, "pristine", ethereum.SyncProgress{})
+
+ // Attempt a full sync with a faulty peer
+ brokenChain := chain.shorten(chain.len())
+ missing := brokenChain.len() / 2
+ delete(brokenChain.headerm, brokenChain.chain[missing])
+ delete(brokenChain.blockm, brokenChain.chain[missing])
+ delete(brokenChain.receiptm, brokenChain.chain[missing])
+ tester.newPeer("faulty", protocol, brokenChain)
+
+ pending := new(sync.WaitGroup)
+ pending.Add(1)
+ go func() {
+ defer pending.Done()
+ if err := tester.sync("faulty", nil, mode); err == nil {
+ panic("succeeded faulty synchronisation")
+ }
+ }()
+ <-starting
+ checkProgress(t, tester.downloader, "initial", ethereum.SyncProgress{
+ HighestBlock: uint64(brokenChain.len() - 1),
+ })
+ progress <- struct{}{}
+ pending.Wait()
+ afterFailedSync := tester.downloader.Progress()
+
+ // Synchronise with a good peer and check that the progress origin remind the same
+ // after a failure
+ tester.newPeer("valid", protocol, chain)
+ pending.Add(1)
+ go func() {
+ defer pending.Done()
+ if err := tester.sync("valid", nil, mode); err != nil {
+ panic(fmt.Sprintf("failed to synchronise blocks: %v", err))
+ }
+ }()
+ <-starting
+ checkProgress(t, tester.downloader, "completing", afterFailedSync)
+
+ // Check final progress after successful sync
+ progress <- struct{}{}
+ pending.Wait()
+ checkProgress(t, tester.downloader, "final", ethereum.SyncProgress{
+ CurrentBlock: uint64(chain.len() - 1),
+ HighestBlock: uint64(chain.len() - 1),
+ })
+}
+
+// Tests that if an attacker fakes a chain height, after the attack is detected,
+// the progress height is successfully reduced at the next sync invocation.
+func TestFakedSyncProgress66Full(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, FullSync) }
+func TestFakedSyncProgress66Fast(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, FastSync) }
+func TestFakedSyncProgress66Light(t *testing.T) { testFakedSyncProgress(t, eth.ETH66, LightSync) }
+
+func testFakedSyncProgress(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ tester := newTester()
+ defer tester.terminate()
+ chain := testChainBase.shorten(blockCacheMaxItems - 15)
+
+ // Set a sync init hook to catch progress changes
+ starting := make(chan struct{})
+ progress := make(chan struct{})
+ tester.downloader.syncInitHook = func(origin, latest uint64) {
+ starting <- struct{}{}
+ <-progress
+ }
+ checkProgress(t, tester.downloader, "pristine", ethereum.SyncProgress{})
+
+ // Create and sync with an attacker that promises a higher chain than available.
+ brokenChain := chain.shorten(chain.len())
+ numMissing := 5
+ for i := brokenChain.len() - 2; i > brokenChain.len()-numMissing; i-- {
+ delete(brokenChain.headerm, brokenChain.chain[i])
+ }
+ tester.newPeer("attack", protocol, brokenChain)
+
+ pending := new(sync.WaitGroup)
+ pending.Add(1)
+ go func() {
+ defer pending.Done()
+ if err := tester.sync("attack", nil, mode); err == nil {
+ panic("succeeded attacker synchronisation")
+ }
+ }()
+ <-starting
+ checkProgress(t, tester.downloader, "initial", ethereum.SyncProgress{
+ HighestBlock: uint64(brokenChain.len() - 1),
+ })
+ progress <- struct{}{}
+ pending.Wait()
+ afterFailedSync := tester.downloader.Progress()
+
+ // Synchronise with a good peer and check that the progress height has been reduced to
+ // the true value.
+ validChain := chain.shorten(chain.len() - numMissing)
+ tester.newPeer("valid", protocol, validChain)
+ pending.Add(1)
+
+ go func() {
+ defer pending.Done()
+ if err := tester.sync("valid", nil, mode); err != nil {
+ panic(fmt.Sprintf("failed to synchronise blocks: %v", err))
+ }
+ }()
+ <-starting
+ checkProgress(t, tester.downloader, "completing", ethereum.SyncProgress{
+ CurrentBlock: afterFailedSync.CurrentBlock,
+ HighestBlock: uint64(validChain.len() - 1),
+ })
+
+ // Check final progress after successful sync.
+ progress <- struct{}{}
+ pending.Wait()
+ checkProgress(t, tester.downloader, "final", ethereum.SyncProgress{
+ CurrentBlock: uint64(validChain.len() - 1),
+ HighestBlock: uint64(validChain.len() - 1),
+ })
+}
+
+// This test reproduces an issue where unexpected deliveries would
+// block indefinitely if they arrived at the right time.
+func TestDeliverHeadersHang66Full(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, FullSync) }
+func TestDeliverHeadersHang66Fast(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, FastSync) }
+func TestDeliverHeadersHang66Light(t *testing.T) { testDeliverHeadersHang(t, eth.ETH66, LightSync) }
+
+func testDeliverHeadersHang(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ master := newTester()
+ defer master.terminate()
+ chain := testChainBase.shorten(15)
+
+ for i := 0; i < 200; i++ {
+ tester := newTester()
+ tester.peerDb = master.peerDb
+ tester.newPeer("peer", protocol, chain)
+
+ // Whenever the downloader requests headers, flood it with
+ // a lot of unrequested header deliveries.
+ tester.downloader.peers.peers["peer"].peer = &floodingTestPeer{
+ peer: tester.downloader.peers.peers["peer"].peer,
+ tester: tester,
+ }
+ if err := tester.sync("peer", nil, mode); err != nil {
+ t.Errorf("test %d: sync failed: %v", i, err)
+ }
+ tester.terminate()
+ }
+}
+
+type floodingTestPeer struct {
+ peer Peer
+ tester *downloadTester
+}
+
+func (ftp *floodingTestPeer) Head() (common.Hash, *big.Int) { return ftp.peer.Head() }
+func (ftp *floodingTestPeer) RequestHeadersByHash(hash common.Hash, count int, skip int, reverse bool) error {
+ return ftp.peer.RequestHeadersByHash(hash, count, skip, reverse)
+}
+func (ftp *floodingTestPeer) RequestBodies(hashes []common.Hash) error {
+ return ftp.peer.RequestBodies(hashes)
+}
+func (ftp *floodingTestPeer) RequestReceipts(hashes []common.Hash) error {
+ return ftp.peer.RequestReceipts(hashes)
+}
+func (ftp *floodingTestPeer) RequestNodeData(hashes []common.Hash) error {
+ return ftp.peer.RequestNodeData(hashes)
+}
+
+func (ftp *floodingTestPeer) RequestHeadersByNumber(from uint64, count, skip int, reverse bool) error {
+ deliveriesDone := make(chan struct{}, 500)
+ for i := 0; i < cap(deliveriesDone)-1; i++ {
+ peer := fmt.Sprintf("fake-peer%d", i)
+ go func() {
+ ftp.tester.downloader.DeliverHeaders(peer, []*types.Header{{}, {}, {}, {}})
+ deliveriesDone <- struct{}{}
+ }()
+ }
+
+ // None of the extra deliveries should block.
+ timeout := time.After(60 * time.Second)
+ launched := false
+ for i := 0; i < cap(deliveriesDone); i++ {
+ select {
+ case <-deliveriesDone:
+ if !launched {
+ // Start delivering the requested headers
+ // after one of the flooding responses has arrived.
+ go func() {
+ ftp.peer.RequestHeadersByNumber(from, count, skip, reverse)
+ deliveriesDone <- struct{}{}
+ }()
+ launched = true
+ }
+ case <-timeout:
+ panic("blocked")
+ }
+ }
+ return nil
+}
+
+func TestRemoteHeaderRequestSpan(t *testing.T) {
+ testCases := []struct {
+ remoteHeight uint64
+ localHeight uint64
+ expected []int
+ }{
+ // Remote is way higher. We should ask for the remote head and go backwards
+ {1500, 1000,
+ []int{1323, 1339, 1355, 1371, 1387, 1403, 1419, 1435, 1451, 1467, 1483, 1499},
+ },
+ {15000, 13006,
+ []int{14823, 14839, 14855, 14871, 14887, 14903, 14919, 14935, 14951, 14967, 14983, 14999},
+ },
+ // Remote is pretty close to us. We don't have to fetch as many
+ {1200, 1150,
+ []int{1149, 1154, 1159, 1164, 1169, 1174, 1179, 1184, 1189, 1194, 1199},
+ },
+ // Remote is equal to us (so on a fork with higher td)
+ // We should get the closest couple of ancestors
+ {1500, 1500,
+ []int{1497, 1499},
+ },
+ // We're higher than the remote! Odd
+ {1000, 1500,
+ []int{997, 999},
+ },
+ // Check some weird edgecases that it behaves somewhat rationally
+ {0, 1500,
+ []int{0, 2},
+ },
+ {6000000, 0,
+ []int{5999823, 5999839, 5999855, 5999871, 5999887, 5999903, 5999919, 5999935, 5999951, 5999967, 5999983, 5999999},
+ },
+ {0, 0,
+ []int{0, 2},
+ },
+ }
+ reqs := func(from, count, span int) []int {
+ var r []int
+ num := from
+ for len(r) < count {
+ r = append(r, num)
+ num += span + 1
+ }
+ return r
+ }
+ for i, tt := range testCases {
+ from, count, span, max := calculateRequestSpan(tt.remoteHeight, tt.localHeight)
+ data := reqs(int(from), count, span)
+
+ if max != uint64(data[len(data)-1]) {
+ t.Errorf("test %d: wrong last value %d != %d", i, data[len(data)-1], max)
+ }
+ failed := false
+ if len(data) != len(tt.expected) {
+ failed = true
+ t.Errorf("test %d: length wrong, expected %d got %d", i, len(tt.expected), len(data))
+ } else {
+ for j, n := range data {
+ if n != tt.expected[j] {
+ failed = true
+ break
+ }
+ }
+ }
+ if failed {
+ res := strings.Replace(fmt.Sprint(data), " ", ",", -1)
+ exp := strings.Replace(fmt.Sprint(tt.expected), " ", ",", -1)
+ t.Logf("got: %v\n", res)
+ t.Logf("exp: %v\n", exp)
+ t.Errorf("test %d: wrong values", i)
+ }
+ }
+}
+
+// Tests that peers below a pre-configured checkpoint block are prevented from
+// being fast-synced from, avoiding potential cheap eclipse attacks.
+func TestCheckpointEnforcement66Full(t *testing.T) { testCheckpointEnforcement(t, eth.ETH66, FullSync) }
+func TestCheckpointEnforcement66Fast(t *testing.T) { testCheckpointEnforcement(t, eth.ETH66, FastSync) }
+func TestCheckpointEnforcement66Light(t *testing.T) {
+ testCheckpointEnforcement(t, eth.ETH66, LightSync)
+}
+
+func testCheckpointEnforcement(t *testing.T, protocol uint, mode SyncMode) {
+ t.Parallel()
+
+ // Create a new tester with a particular hard coded checkpoint block
+ tester := newTester()
+ defer tester.terminate()
+
+ tester.downloader.checkpoint = uint64(fsMinFullBlocks) + 256
+ chain := testChainBase.shorten(int(tester.downloader.checkpoint) - 1)
+
+ // Attempt to sync with the peer and validate the result
+ tester.newPeer("peer", protocol, chain)
+
+ var expect error
+ if mode == FastSync || mode == LightSync {
+ expect = errUnsyncedPeer
+ }
+ if err := tester.sync("peer", nil, mode); !errors.Is(err, expect) {
+ t.Fatalf("block sync error mismatch: have %v, want %v", err, expect)
+ }
+ if mode == FastSync || mode == LightSync {
+ assertOwnChain(t, tester, 1)
+ } else {
+ assertOwnChain(t, tester, chain.len())
+ }
+}
diff --git a/signer/rules/deps/deps.go b/les/downloader/events.go
similarity index 75%
rename from signer/rules/deps/deps.go
rename to les/downloader/events.go
index 5ee00b900a..25255a3a72 100644
--- a/signer/rules/deps/deps.go
+++ b/les/downloader/events.go
@@ -1,4 +1,4 @@
-// Copyright 2018 The go-ethereum Authors
+// 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
@@ -14,8 +14,12 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
-// Package deps contains the console JavaScript dependencies Go embedded.
-package deps
+package downloader
-//go:generate go-bindata -nometadata -pkg deps -o bindata.go bignumber.js
-//go:generate gofmt -w -s bindata.go
+import "github.com/ethereum/go-ethereum/core/types"
+
+type DoneEvent struct {
+ Latest *types.Header
+}
+type StartEvent struct{}
+type FailedEvent struct{ Err error }
diff --git a/les/downloader/metrics.go b/les/downloader/metrics.go
new file mode 100644
index 0000000000..c38732043a
--- /dev/null
+++ b/les/downloader/metrics.go
@@ -0,0 +1,45 @@
+// 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 .
+
+// Contains the metrics collected by the downloader.
+
+package downloader
+
+import (
+ "github.com/ethereum/go-ethereum/metrics"
+)
+
+var (
+ headerInMeter = metrics.NewRegisteredMeter("eth/downloader/headers/in", nil)
+ headerReqTimer = metrics.NewRegisteredTimer("eth/downloader/headers/req", nil)
+ headerDropMeter = metrics.NewRegisteredMeter("eth/downloader/headers/drop", nil)
+ headerTimeoutMeter = metrics.NewRegisteredMeter("eth/downloader/headers/timeout", nil)
+
+ bodyInMeter = metrics.NewRegisteredMeter("eth/downloader/bodies/in", nil)
+ bodyReqTimer = metrics.NewRegisteredTimer("eth/downloader/bodies/req", nil)
+ bodyDropMeter = metrics.NewRegisteredMeter("eth/downloader/bodies/drop", nil)
+ bodyTimeoutMeter = metrics.NewRegisteredMeter("eth/downloader/bodies/timeout", nil)
+
+ receiptInMeter = metrics.NewRegisteredMeter("eth/downloader/receipts/in", nil)
+ receiptReqTimer = metrics.NewRegisteredTimer("eth/downloader/receipts/req", nil)
+ receiptDropMeter = metrics.NewRegisteredMeter("eth/downloader/receipts/drop", nil)
+ receiptTimeoutMeter = metrics.NewRegisteredMeter("eth/downloader/receipts/timeout", nil)
+
+ stateInMeter = metrics.NewRegisteredMeter("eth/downloader/states/in", nil)
+ stateDropMeter = metrics.NewRegisteredMeter("eth/downloader/states/drop", nil)
+
+ throttleCounter = metrics.NewRegisteredCounter("eth/downloader/throttle", nil)
+)
diff --git a/les/downloader/modes.go b/les/downloader/modes.go
new file mode 100644
index 0000000000..3ea14d22d7
--- /dev/null
+++ b/les/downloader/modes.go
@@ -0,0 +1,81 @@
+// 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 downloader
+
+import "fmt"
+
+// SyncMode represents the synchronisation mode of the downloader.
+// It is a uint32 as it is used with atomic operations.
+type SyncMode uint32
+
+const (
+ FullSync SyncMode = iota // Synchronise the entire blockchain history from full blocks
+ FastSync // Quickly download the headers, full sync only at the chain
+ SnapSync // Download the chain and the state via compact snapshots
+ LightSync // Download only the headers and terminate afterwards
+)
+
+func (mode SyncMode) IsValid() bool {
+ return mode >= FullSync && mode <= LightSync
+}
+
+// String implements the stringer interface.
+func (mode SyncMode) String() string {
+ switch mode {
+ case FullSync:
+ return "full"
+ case FastSync:
+ return "fast"
+ case SnapSync:
+ return "snap"
+ case LightSync:
+ return "light"
+ default:
+ return "unknown"
+ }
+}
+
+func (mode SyncMode) MarshalText() ([]byte, error) {
+ switch mode {
+ case FullSync:
+ return []byte("full"), nil
+ case FastSync:
+ return []byte("fast"), nil
+ case SnapSync:
+ return []byte("snap"), nil
+ case LightSync:
+ return []byte("light"), nil
+ default:
+ return nil, fmt.Errorf("unknown sync mode %d", mode)
+ }
+}
+
+func (mode *SyncMode) UnmarshalText(text []byte) error {
+ switch string(text) {
+ case "full":
+ *mode = FullSync
+ case "fast":
+ *mode = FastSync
+ case "snap":
+ *mode = SnapSync
+ case "light":
+ *mode = LightSync
+ default:
+ return fmt.Errorf(`unknown sync mode %q, want "full", "fast" or "light"`, text)
+ }
+ return nil
+}
diff --git a/les/downloader/peer.go b/les/downloader/peer.go
new file mode 100644
index 0000000000..8632948329
--- /dev/null
+++ b/les/downloader/peer.go
@@ -0,0 +1,501 @@
+// 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 .
+
+// Contains the active peer-set of the downloader, maintaining both failures
+// as well as reputation metrics to prioritize the block retrievals.
+
+package downloader
+
+import (
+ "errors"
+ "math/big"
+ "sort"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/eth/protocols/eth"
+ "github.com/ethereum/go-ethereum/event"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/p2p/msgrate"
+)
+
+const (
+ maxLackingHashes = 4096 // Maximum number of entries allowed on the list or lacking items
+)
+
+var (
+ errAlreadyFetching = errors.New("already fetching blocks from peer")
+ errAlreadyRegistered = errors.New("peer is already registered")
+ errNotRegistered = errors.New("peer is not registered")
+)
+
+// peerConnection represents an active peer from which hashes and blocks are retrieved.
+type peerConnection struct {
+ id string // Unique identifier of the peer
+
+ headerIdle int32 // Current header activity state of the peer (idle = 0, active = 1)
+ blockIdle int32 // Current block activity state of the peer (idle = 0, active = 1)
+ receiptIdle int32 // Current receipt activity state of the peer (idle = 0, active = 1)
+ stateIdle int32 // Current node data activity state of the peer (idle = 0, active = 1)
+
+ headerStarted time.Time // Time instance when the last header fetch was started
+ blockStarted time.Time // Time instance when the last block (body) fetch was started
+ receiptStarted time.Time // Time instance when the last receipt fetch was started
+ stateStarted time.Time // Time instance when the last node data fetch was started
+
+ rates *msgrate.Tracker // Tracker to hone in on the number of items retrievable per second
+ lacking map[common.Hash]struct{} // Set of hashes not to request (didn't have previously)
+
+ peer Peer
+
+ version uint // Eth protocol version number to switch strategies
+ log log.Logger // Contextual logger to add extra infos to peer logs
+ lock sync.RWMutex
+}
+
+// LightPeer encapsulates the methods required to synchronise with a remote light peer.
+type LightPeer interface {
+ Head() (common.Hash, *big.Int)
+ RequestHeadersByHash(common.Hash, int, int, bool) error
+ RequestHeadersByNumber(uint64, int, int, bool) error
+}
+
+// Peer encapsulates the methods required to synchronise with a remote full peer.
+type Peer interface {
+ LightPeer
+ RequestBodies([]common.Hash) error
+ RequestReceipts([]common.Hash) error
+ RequestNodeData([]common.Hash) error
+}
+
+// lightPeerWrapper wraps a LightPeer struct, stubbing out the Peer-only methods.
+type lightPeerWrapper struct {
+ peer LightPeer
+}
+
+func (w *lightPeerWrapper) Head() (common.Hash, *big.Int) { return w.peer.Head() }
+func (w *lightPeerWrapper) RequestHeadersByHash(h common.Hash, amount int, skip int, reverse bool) error {
+ return w.peer.RequestHeadersByHash(h, amount, skip, reverse)
+}
+func (w *lightPeerWrapper) RequestHeadersByNumber(i uint64, amount int, skip int, reverse bool) error {
+ return w.peer.RequestHeadersByNumber(i, amount, skip, reverse)
+}
+func (w *lightPeerWrapper) RequestBodies([]common.Hash) error {
+ panic("RequestBodies not supported in light client mode sync")
+}
+func (w *lightPeerWrapper) RequestReceipts([]common.Hash) error {
+ panic("RequestReceipts not supported in light client mode sync")
+}
+func (w *lightPeerWrapper) RequestNodeData([]common.Hash) error {
+ panic("RequestNodeData not supported in light client mode sync")
+}
+
+// newPeerConnection creates a new downloader peer.
+func newPeerConnection(id string, version uint, peer Peer, logger log.Logger) *peerConnection {
+ return &peerConnection{
+ id: id,
+ lacking: make(map[common.Hash]struct{}),
+ peer: peer,
+ version: version,
+ log: logger,
+ }
+}
+
+// Reset clears the internal state of a peer entity.
+func (p *peerConnection) Reset() {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ atomic.StoreInt32(&p.headerIdle, 0)
+ atomic.StoreInt32(&p.blockIdle, 0)
+ atomic.StoreInt32(&p.receiptIdle, 0)
+ atomic.StoreInt32(&p.stateIdle, 0)
+
+ p.lacking = make(map[common.Hash]struct{})
+}
+
+// FetchHeaders sends a header retrieval request to the remote peer.
+func (p *peerConnection) FetchHeaders(from uint64, count int) error {
+ // Short circuit if the peer is already fetching
+ if !atomic.CompareAndSwapInt32(&p.headerIdle, 0, 1) {
+ return errAlreadyFetching
+ }
+ p.headerStarted = time.Now()
+
+ // Issue the header retrieval request (absolute upwards without gaps)
+ go p.peer.RequestHeadersByNumber(from, count, 0, false)
+
+ return nil
+}
+
+// FetchBodies sends a block body retrieval request to the remote peer.
+func (p *peerConnection) FetchBodies(request *fetchRequest) error {
+ // Short circuit if the peer is already fetching
+ if !atomic.CompareAndSwapInt32(&p.blockIdle, 0, 1) {
+ return errAlreadyFetching
+ }
+ p.blockStarted = time.Now()
+
+ go func() {
+ // Convert the header set to a retrievable slice
+ hashes := make([]common.Hash, 0, len(request.Headers))
+ for _, header := range request.Headers {
+ hashes = append(hashes, header.Hash())
+ }
+ p.peer.RequestBodies(hashes)
+ }()
+
+ return nil
+}
+
+// FetchReceipts sends a receipt retrieval request to the remote peer.
+func (p *peerConnection) FetchReceipts(request *fetchRequest) error {
+ // Short circuit if the peer is already fetching
+ if !atomic.CompareAndSwapInt32(&p.receiptIdle, 0, 1) {
+ return errAlreadyFetching
+ }
+ p.receiptStarted = time.Now()
+
+ go func() {
+ // Convert the header set to a retrievable slice
+ hashes := make([]common.Hash, 0, len(request.Headers))
+ for _, header := range request.Headers {
+ hashes = append(hashes, header.Hash())
+ }
+ p.peer.RequestReceipts(hashes)
+ }()
+
+ return nil
+}
+
+// FetchNodeData sends a node state data retrieval request to the remote peer.
+func (p *peerConnection) FetchNodeData(hashes []common.Hash) error {
+ // Short circuit if the peer is already fetching
+ if !atomic.CompareAndSwapInt32(&p.stateIdle, 0, 1) {
+ return errAlreadyFetching
+ }
+ p.stateStarted = time.Now()
+
+ go p.peer.RequestNodeData(hashes)
+
+ return nil
+}
+
+// SetHeadersIdle sets the peer to idle, allowing it to execute new header retrieval
+// requests. Its estimated header retrieval throughput is updated with that measured
+// just now.
+func (p *peerConnection) SetHeadersIdle(delivered int, deliveryTime time.Time) {
+ p.rates.Update(eth.BlockHeadersMsg, deliveryTime.Sub(p.headerStarted), delivered)
+ atomic.StoreInt32(&p.headerIdle, 0)
+}
+
+// SetBodiesIdle sets the peer to idle, allowing it to execute block body retrieval
+// requests. Its estimated body retrieval throughput is updated with that measured
+// just now.
+func (p *peerConnection) SetBodiesIdle(delivered int, deliveryTime time.Time) {
+ p.rates.Update(eth.BlockBodiesMsg, deliveryTime.Sub(p.blockStarted), delivered)
+ atomic.StoreInt32(&p.blockIdle, 0)
+}
+
+// SetReceiptsIdle sets the peer to idle, allowing it to execute new receipt
+// retrieval requests. Its estimated receipt retrieval throughput is updated
+// with that measured just now.
+func (p *peerConnection) SetReceiptsIdle(delivered int, deliveryTime time.Time) {
+ p.rates.Update(eth.ReceiptsMsg, deliveryTime.Sub(p.receiptStarted), delivered)
+ atomic.StoreInt32(&p.receiptIdle, 0)
+}
+
+// SetNodeDataIdle sets the peer to idle, allowing it to execute new state trie
+// data retrieval requests. Its estimated state retrieval throughput is updated
+// with that measured just now.
+func (p *peerConnection) SetNodeDataIdle(delivered int, deliveryTime time.Time) {
+ p.rates.Update(eth.NodeDataMsg, deliveryTime.Sub(p.stateStarted), delivered)
+ atomic.StoreInt32(&p.stateIdle, 0)
+}
+
+// HeaderCapacity retrieves the peers header download allowance based on its
+// previously discovered throughput.
+func (p *peerConnection) HeaderCapacity(targetRTT time.Duration) int {
+ cap := p.rates.Capacity(eth.BlockHeadersMsg, targetRTT)
+ if cap > MaxHeaderFetch {
+ cap = MaxHeaderFetch
+ }
+ return cap
+}
+
+// BlockCapacity retrieves the peers block download allowance based on its
+// previously discovered throughput.
+func (p *peerConnection) BlockCapacity(targetRTT time.Duration) int {
+ cap := p.rates.Capacity(eth.BlockBodiesMsg, targetRTT)
+ if cap > MaxBlockFetch {
+ cap = MaxBlockFetch
+ }
+ return cap
+}
+
+// ReceiptCapacity retrieves the peers receipt download allowance based on its
+// previously discovered throughput.
+func (p *peerConnection) ReceiptCapacity(targetRTT time.Duration) int {
+ cap := p.rates.Capacity(eth.ReceiptsMsg, targetRTT)
+ if cap > MaxReceiptFetch {
+ cap = MaxReceiptFetch
+ }
+ return cap
+}
+
+// NodeDataCapacity retrieves the peers state download allowance based on its
+// previously discovered throughput.
+func (p *peerConnection) NodeDataCapacity(targetRTT time.Duration) int {
+ cap := p.rates.Capacity(eth.NodeDataMsg, targetRTT)
+ if cap > MaxStateFetch {
+ cap = MaxStateFetch
+ }
+ return cap
+}
+
+// MarkLacking appends a new entity to the set of items (blocks, receipts, states)
+// that a peer is known not to have (i.e. have been requested before). If the
+// set reaches its maximum allowed capacity, items are randomly dropped off.
+func (p *peerConnection) MarkLacking(hash common.Hash) {
+ p.lock.Lock()
+ defer p.lock.Unlock()
+
+ for len(p.lacking) >= maxLackingHashes {
+ for drop := range p.lacking {
+ delete(p.lacking, drop)
+ break
+ }
+ }
+ p.lacking[hash] = struct{}{}
+}
+
+// Lacks retrieves whether the hash of a blockchain item is on the peers lacking
+// list (i.e. whether we know that the peer does not have it).
+func (p *peerConnection) Lacks(hash common.Hash) bool {
+ p.lock.RLock()
+ defer p.lock.RUnlock()
+
+ _, ok := p.lacking[hash]
+ return ok
+}
+
+// peerSet represents the collection of active peer participating in the chain
+// download procedure.
+type peerSet struct {
+ peers map[string]*peerConnection
+ rates *msgrate.Trackers // Set of rate trackers to give the sync a common beat
+
+ newPeerFeed event.Feed
+ peerDropFeed event.Feed
+
+ lock sync.RWMutex
+}
+
+// newPeerSet creates a new peer set top track the active download sources.
+func newPeerSet() *peerSet {
+ return &peerSet{
+ peers: make(map[string]*peerConnection),
+ rates: msgrate.NewTrackers(log.New("proto", "eth")),
+ }
+}
+
+// SubscribeNewPeers subscribes to peer arrival events.
+func (ps *peerSet) SubscribeNewPeers(ch chan<- *peerConnection) event.Subscription {
+ return ps.newPeerFeed.Subscribe(ch)
+}
+
+// SubscribePeerDrops subscribes to peer departure events.
+func (ps *peerSet) SubscribePeerDrops(ch chan<- *peerConnection) event.Subscription {
+ return ps.peerDropFeed.Subscribe(ch)
+}
+
+// Reset iterates over the current peer set, and resets each of the known peers
+// to prepare for a next batch of block retrieval.
+func (ps *peerSet) Reset() {
+ ps.lock.RLock()
+ defer ps.lock.RUnlock()
+
+ for _, peer := range ps.peers {
+ peer.Reset()
+ }
+}
+
+// Register injects a new peer into the working set, or returns an error if the
+// peer is already known.
+//
+// The method also sets the starting throughput values of the new peer to the
+// average of all existing peers, to give it a realistic chance of being used
+// for data retrievals.
+func (ps *peerSet) Register(p *peerConnection) error {
+ // Register the new peer with some meaningful defaults
+ ps.lock.Lock()
+ if _, ok := ps.peers[p.id]; ok {
+ ps.lock.Unlock()
+ return errAlreadyRegistered
+ }
+ p.rates = msgrate.NewTracker(ps.rates.MeanCapacities(), ps.rates.MedianRoundTrip())
+ if err := ps.rates.Track(p.id, p.rates); err != nil {
+ return err
+ }
+ ps.peers[p.id] = p
+ ps.lock.Unlock()
+
+ ps.newPeerFeed.Send(p)
+ return nil
+}
+
+// Unregister removes a remote peer from the active set, disabling any further
+// actions to/from that particular entity.
+func (ps *peerSet) Unregister(id string) error {
+ ps.lock.Lock()
+ p, ok := ps.peers[id]
+ if !ok {
+ ps.lock.Unlock()
+ return errNotRegistered
+ }
+ delete(ps.peers, id)
+ ps.rates.Untrack(id)
+ ps.lock.Unlock()
+
+ ps.peerDropFeed.Send(p)
+ return nil
+}
+
+// Peer retrieves the registered peer with the given id.
+func (ps *peerSet) Peer(id string) *peerConnection {
+ ps.lock.RLock()
+ defer ps.lock.RUnlock()
+
+ return ps.peers[id]
+}
+
+// Len returns if the current number of peers in the set.
+func (ps *peerSet) Len() int {
+ ps.lock.RLock()
+ defer ps.lock.RUnlock()
+
+ return len(ps.peers)
+}
+
+// AllPeers retrieves a flat list of all the peers within the set.
+func (ps *peerSet) AllPeers() []*peerConnection {
+ ps.lock.RLock()
+ defer ps.lock.RUnlock()
+
+ list := make([]*peerConnection, 0, len(ps.peers))
+ for _, p := range ps.peers {
+ list = append(list, p)
+ }
+ return list
+}
+
+// HeaderIdlePeers retrieves a flat list of all the currently header-idle peers
+// within the active peer set, ordered by their reputation.
+func (ps *peerSet) HeaderIdlePeers() ([]*peerConnection, int) {
+ idle := func(p *peerConnection) bool {
+ return atomic.LoadInt32(&p.headerIdle) == 0
+ }
+ throughput := func(p *peerConnection) int {
+ return p.rates.Capacity(eth.BlockHeadersMsg, time.Second)
+ }
+ return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput)
+}
+
+// BodyIdlePeers retrieves a flat list of all the currently body-idle peers within
+// the active peer set, ordered by their reputation.
+func (ps *peerSet) BodyIdlePeers() ([]*peerConnection, int) {
+ idle := func(p *peerConnection) bool {
+ return atomic.LoadInt32(&p.blockIdle) == 0
+ }
+ throughput := func(p *peerConnection) int {
+ return p.rates.Capacity(eth.BlockBodiesMsg, time.Second)
+ }
+ return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput)
+}
+
+// ReceiptIdlePeers retrieves a flat list of all the currently receipt-idle peers
+// within the active peer set, ordered by their reputation.
+func (ps *peerSet) ReceiptIdlePeers() ([]*peerConnection, int) {
+ idle := func(p *peerConnection) bool {
+ return atomic.LoadInt32(&p.receiptIdle) == 0
+ }
+ throughput := func(p *peerConnection) int {
+ return p.rates.Capacity(eth.ReceiptsMsg, time.Second)
+ }
+ return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput)
+}
+
+// NodeDataIdlePeers retrieves a flat list of all the currently node-data-idle
+// peers within the active peer set, ordered by their reputation.
+func (ps *peerSet) NodeDataIdlePeers() ([]*peerConnection, int) {
+ idle := func(p *peerConnection) bool {
+ return atomic.LoadInt32(&p.stateIdle) == 0
+ }
+ throughput := func(p *peerConnection) int {
+ return p.rates.Capacity(eth.NodeDataMsg, time.Second)
+ }
+ return ps.idlePeers(eth.ETH66, eth.ETH66, idle, throughput)
+}
+
+// idlePeers retrieves a flat list of all currently idle peers satisfying the
+// protocol version constraints, using the provided function to check idleness.
+// The resulting set of peers are sorted by their capacity.
+func (ps *peerSet) idlePeers(minProtocol, maxProtocol uint, idleCheck func(*peerConnection) bool, capacity func(*peerConnection) int) ([]*peerConnection, int) {
+ ps.lock.RLock()
+ defer ps.lock.RUnlock()
+
+ var (
+ total = 0
+ idle = make([]*peerConnection, 0, len(ps.peers))
+ tps = make([]int, 0, len(ps.peers))
+ )
+ for _, p := range ps.peers {
+ if p.version >= minProtocol && p.version <= maxProtocol {
+ if idleCheck(p) {
+ idle = append(idle, p)
+ tps = append(tps, capacity(p))
+ }
+ total++
+ }
+ }
+
+ // And sort them
+ sortPeers := &peerCapacitySort{idle, tps}
+ sort.Sort(sortPeers)
+ return sortPeers.p, total
+}
+
+// peerCapacitySort implements sort.Interface.
+// It sorts peer connections by capacity (descending).
+type peerCapacitySort struct {
+ p []*peerConnection
+ tp []int
+}
+
+func (ps *peerCapacitySort) Len() int {
+ return len(ps.p)
+}
+
+func (ps *peerCapacitySort) Less(i, j int) bool {
+ return ps.tp[i] > ps.tp[j]
+}
+
+func (ps *peerCapacitySort) Swap(i, j int) {
+ ps.p[i], ps.p[j] = ps.p[j], ps.p[i]
+ ps.tp[i], ps.tp[j] = ps.tp[j], ps.tp[i]
+}
diff --git a/les/downloader/queue.go b/les/downloader/queue.go
new file mode 100644
index 0000000000..04ec12cfa9
--- /dev/null
+++ b/les/downloader/queue.go
@@ -0,0 +1,913 @@
+// 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 .
+
+// Contains the block download scheduler to collect download tasks and schedule
+// them in an ordered, and throttled way.
+
+package downloader
+
+import (
+ "errors"
+ "fmt"
+ "sync"
+ "sync/atomic"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/prque"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/metrics"
+ "github.com/ethereum/go-ethereum/trie"
+)
+
+const (
+ bodyType = uint(0)
+ receiptType = uint(1)
+)
+
+var (
+ blockCacheMaxItems = 8192 // Maximum number of blocks to cache before throttling the download
+ blockCacheInitialItems = 2048 // Initial number of blocks to start fetching, before we know the sizes of the blocks
+ blockCacheMemory = 256 * 1024 * 1024 // Maximum amount of memory to use for block caching
+ blockCacheSizeWeight = 0.1 // Multiplier to approximate the average block size based on past ones
+)
+
+var (
+ errNoFetchesPending = errors.New("no fetches pending")
+ errStaleDelivery = errors.New("stale delivery")
+)
+
+// fetchRequest is a currently running data retrieval operation.
+type fetchRequest struct {
+ Peer *peerConnection // Peer to which the request was sent
+ From uint64 // [eth/62] Requested chain element index (used for skeleton fills only)
+ Headers []*types.Header // [eth/62] Requested headers, sorted by request order
+ Time time.Time // Time when the request was made
+}
+
+// fetchResult is a struct collecting partial results from data fetchers until
+// all outstanding pieces complete and the result as a whole can be processed.
+type fetchResult struct {
+ pending int32 // Flag telling what deliveries are outstanding
+
+ Header *types.Header
+ Uncles []*types.Header
+ Transactions types.Transactions
+ Receipts types.Receipts
+}
+
+func newFetchResult(header *types.Header, fastSync bool) *fetchResult {
+ item := &fetchResult{
+ Header: header,
+ }
+ if !header.EmptyBody() {
+ item.pending |= (1 << bodyType)
+ }
+ if fastSync && !header.EmptyReceipts() {
+ item.pending |= (1 << receiptType)
+ }
+ return item
+}
+
+// SetBodyDone flags the body as finished.
+func (f *fetchResult) SetBodyDone() {
+ if v := atomic.LoadInt32(&f.pending); (v & (1 << bodyType)) != 0 {
+ atomic.AddInt32(&f.pending, -1)
+ }
+}
+
+// AllDone checks if item is done.
+func (f *fetchResult) AllDone() bool {
+ return atomic.LoadInt32(&f.pending) == 0
+}
+
+// SetReceiptsDone flags the receipts as finished.
+func (f *fetchResult) SetReceiptsDone() {
+ if v := atomic.LoadInt32(&f.pending); (v & (1 << receiptType)) != 0 {
+ atomic.AddInt32(&f.pending, -2)
+ }
+}
+
+// Done checks if the given type is done already
+func (f *fetchResult) Done(kind uint) bool {
+ v := atomic.LoadInt32(&f.pending)
+ return v&(1< 0
+}
+
+// InFlightBlocks retrieves whether there are block fetch requests currently in
+// flight.
+func (q *queue) InFlightBlocks() bool {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ return len(q.blockPendPool) > 0
+}
+
+// InFlightReceipts retrieves whether there are receipt fetch requests currently
+// in flight.
+func (q *queue) InFlightReceipts() bool {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ return len(q.receiptPendPool) > 0
+}
+
+// Idle returns if the queue is fully idle or has some data still inside.
+func (q *queue) Idle() bool {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ queued := q.blockTaskQueue.Size() + q.receiptTaskQueue.Size()
+ pending := len(q.blockPendPool) + len(q.receiptPendPool)
+
+ return (queued + pending) == 0
+}
+
+// ScheduleSkeleton adds a batch of header retrieval tasks to the queue to fill
+// up an already retrieved header skeleton.
+func (q *queue) ScheduleSkeleton(from uint64, skeleton []*types.Header) {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ // No skeleton retrieval can be in progress, fail hard if so (huge implementation bug)
+ if q.headerResults != nil {
+ panic("skeleton assembly already in progress")
+ }
+ // Schedule all the header retrieval tasks for the skeleton assembly
+ q.headerTaskPool = make(map[uint64]*types.Header)
+ q.headerTaskQueue = prque.New(nil)
+ q.headerPeerMiss = make(map[string]map[uint64]struct{}) // Reset availability to correct invalid chains
+ q.headerResults = make([]*types.Header, len(skeleton)*MaxHeaderFetch)
+ q.headerProced = 0
+ q.headerOffset = from
+ q.headerContCh = make(chan bool, 1)
+
+ for i, header := range skeleton {
+ index := from + uint64(i*MaxHeaderFetch)
+
+ q.headerTaskPool[index] = header
+ q.headerTaskQueue.Push(index, -int64(index))
+ }
+}
+
+// RetrieveHeaders retrieves the header chain assemble based on the scheduled
+// skeleton.
+func (q *queue) RetrieveHeaders() ([]*types.Header, int) {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ headers, proced := q.headerResults, q.headerProced
+ q.headerResults, q.headerProced = nil, 0
+
+ return headers, proced
+}
+
+// Schedule adds a set of headers for the download queue for scheduling, returning
+// the new headers encountered.
+func (q *queue) Schedule(headers []*types.Header, from uint64) []*types.Header {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ // Insert all the headers prioritised by the contained block number
+ inserts := make([]*types.Header, 0, len(headers))
+ for _, header := range headers {
+ // Make sure chain order is honoured and preserved throughout
+ hash := header.Hash()
+ if header.Number == nil || header.Number.Uint64() != from {
+ log.Warn("Header broke chain ordering", "number", header.Number, "hash", hash, "expected", from)
+ break
+ }
+ if q.headerHead != (common.Hash{}) && q.headerHead != header.ParentHash {
+ log.Warn("Header broke chain ancestry", "number", header.Number, "hash", hash)
+ break
+ }
+ // Make sure no duplicate requests are executed
+ // We cannot skip this, even if the block is empty, since this is
+ // what triggers the fetchResult creation.
+ if _, ok := q.blockTaskPool[hash]; ok {
+ log.Warn("Header already scheduled for block fetch", "number", header.Number, "hash", hash)
+ } else {
+ q.blockTaskPool[hash] = header
+ q.blockTaskQueue.Push(header, -int64(header.Number.Uint64()))
+ }
+ // Queue for receipt retrieval
+ if q.mode == FastSync && !header.EmptyReceipts() {
+ if _, ok := q.receiptTaskPool[hash]; ok {
+ log.Warn("Header already scheduled for receipt fetch", "number", header.Number, "hash", hash)
+ } else {
+ q.receiptTaskPool[hash] = header
+ q.receiptTaskQueue.Push(header, -int64(header.Number.Uint64()))
+ }
+ }
+ inserts = append(inserts, header)
+ q.headerHead = hash
+ from++
+ }
+ return inserts
+}
+
+// Results retrieves and permanently removes a batch of fetch results from
+// the cache. the result slice will be empty if the queue has been closed.
+// Results can be called concurrently with Deliver and Schedule,
+// but assumes that there are not two simultaneous callers to Results
+func (q *queue) Results(block bool) []*fetchResult {
+ // Abort early if there are no items and non-blocking requested
+ if !block && !q.resultCache.HasCompletedItems() {
+ return nil
+ }
+ closed := false
+ for !closed && !q.resultCache.HasCompletedItems() {
+ // In order to wait on 'active', we need to obtain the lock.
+ // That may take a while, if someone is delivering at the same
+ // time, so after obtaining the lock, we check again if there
+ // are any results to fetch.
+ // Also, in-between we ask for the lock and the lock is obtained,
+ // someone can have closed the queue. In that case, we should
+ // return the available results and stop blocking
+ q.lock.Lock()
+ if q.resultCache.HasCompletedItems() || q.closed {
+ q.lock.Unlock()
+ break
+ }
+ // No items available, and not closed
+ q.active.Wait()
+ closed = q.closed
+ q.lock.Unlock()
+ }
+ // Regardless if closed or not, we can still deliver whatever we have
+ results := q.resultCache.GetCompleted(maxResultsProcess)
+ for _, result := range results {
+ // Recalculate the result item weights to prevent memory exhaustion
+ size := result.Header.Size()
+ for _, uncle := range result.Uncles {
+ size += uncle.Size()
+ }
+ for _, receipt := range result.Receipts {
+ size += receipt.Size()
+ }
+ for _, tx := range result.Transactions {
+ size += tx.Size()
+ }
+ q.resultSize = common.StorageSize(blockCacheSizeWeight)*size +
+ (1-common.StorageSize(blockCacheSizeWeight))*q.resultSize
+ }
+ // Using the newly calibrated resultsize, figure out the new throttle limit
+ // on the result cache
+ throttleThreshold := uint64((common.StorageSize(blockCacheMemory) + q.resultSize - 1) / q.resultSize)
+ throttleThreshold = q.resultCache.SetThrottleThreshold(throttleThreshold)
+
+ // Log some info at certain times
+ if time.Since(q.lastStatLog) > 60*time.Second {
+ q.lastStatLog = time.Now()
+ info := q.Stats()
+ info = append(info, "throttle", throttleThreshold)
+ log.Info("Downloader queue stats", info...)
+ }
+ return results
+}
+
+func (q *queue) Stats() []interface{} {
+ q.lock.RLock()
+ defer q.lock.RUnlock()
+
+ return q.stats()
+}
+
+func (q *queue) stats() []interface{} {
+ return []interface{}{
+ "receiptTasks", q.receiptTaskQueue.Size(),
+ "blockTasks", q.blockTaskQueue.Size(),
+ "itemSize", q.resultSize,
+ }
+}
+
+// ReserveHeaders reserves a set of headers for the given peer, skipping any
+// previously failed batches.
+func (q *queue) ReserveHeaders(p *peerConnection, count int) *fetchRequest {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ // Short circuit if the peer's already downloading something (sanity check to
+ // not corrupt state)
+ if _, ok := q.headerPendPool[p.id]; ok {
+ return nil
+ }
+ // Retrieve a batch of hashes, skipping previously failed ones
+ send, skip := uint64(0), []uint64{}
+ for send == 0 && !q.headerTaskQueue.Empty() {
+ from, _ := q.headerTaskQueue.Pop()
+ if q.headerPeerMiss[p.id] != nil {
+ if _, ok := q.headerPeerMiss[p.id][from.(uint64)]; ok {
+ skip = append(skip, from.(uint64))
+ continue
+ }
+ }
+ send = from.(uint64)
+ }
+ // Merge all the skipped batches back
+ for _, from := range skip {
+ q.headerTaskQueue.Push(from, -int64(from))
+ }
+ // Assemble and return the block download request
+ if send == 0 {
+ return nil
+ }
+ request := &fetchRequest{
+ Peer: p,
+ From: send,
+ Time: time.Now(),
+ }
+ q.headerPendPool[p.id] = request
+ return request
+}
+
+// ReserveBodies reserves a set of body fetches for the given peer, skipping any
+// previously failed downloads. Beside the next batch of needed fetches, it also
+// returns a flag whether empty blocks were queued requiring processing.
+func (q *queue) ReserveBodies(p *peerConnection, count int) (*fetchRequest, bool, bool) {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ return q.reserveHeaders(p, count, q.blockTaskPool, q.blockTaskQueue, q.blockPendPool, bodyType)
+}
+
+// ReserveReceipts reserves a set of receipt fetches for the given peer, skipping
+// any previously failed downloads. Beside the next batch of needed fetches, it
+// also returns a flag whether empty receipts were queued requiring importing.
+func (q *queue) ReserveReceipts(p *peerConnection, count int) (*fetchRequest, bool, bool) {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ return q.reserveHeaders(p, count, q.receiptTaskPool, q.receiptTaskQueue, q.receiptPendPool, receiptType)
+}
+
+// reserveHeaders reserves a set of data download operations for a given peer,
+// skipping any previously failed ones. This method is a generic version used
+// by the individual special reservation functions.
+//
+// Note, this method expects the queue lock to be already held for writing. 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.
+//
+// Returns:
+// item - the fetchRequest
+// progress - whether any progress was made
+// throttle - if the caller should throttle for a while
+func (q *queue) reserveHeaders(p *peerConnection, count int, taskPool map[common.Hash]*types.Header, taskQueue *prque.Prque,
+ pendPool map[string]*fetchRequest, kind uint) (*fetchRequest, bool, bool) {
+ // Short circuit if the pool has been depleted, or if the peer's already
+ // downloading something (sanity check not to corrupt state)
+ if taskQueue.Empty() {
+ return nil, false, true
+ }
+ if _, ok := pendPool[p.id]; ok {
+ return nil, false, false
+ }
+ // Retrieve a batch of tasks, skipping previously failed ones
+ send := make([]*types.Header, 0, count)
+ skip := make([]*types.Header, 0)
+ progress := false
+ throttled := false
+ for proc := 0; len(send) < count && !taskQueue.Empty(); proc++ {
+ // the task queue will pop items in order, so the highest prio block
+ // is also the lowest block number.
+ h, _ := taskQueue.Peek()
+ header := h.(*types.Header)
+ // we can ask the resultcache if this header is within the
+ // "prioritized" segment of blocks. If it is not, we need to throttle
+
+ stale, throttle, item, err := q.resultCache.AddFetch(header, q.mode == FastSync)
+ if stale {
+ // Don't put back in the task queue, this item has already been
+ // delivered upstream
+ taskQueue.PopItem()
+ progress = true
+ delete(taskPool, header.Hash())
+ proc = proc - 1
+ log.Error("Fetch reservation already delivered", "number", header.Number.Uint64())
+ continue
+ }
+ if throttle {
+ // There are no resultslots available. Leave it in the task queue
+ // However, if there are any left as 'skipped', we should not tell
+ // the caller to throttle, since we still want some other
+ // peer to fetch those for us
+ throttled = len(skip) == 0
+ break
+ }
+ if err != nil {
+ // this most definitely should _not_ happen
+ log.Warn("Failed to reserve headers", "err", err)
+ // There are no resultslots available. Leave it in the task queue
+ break
+ }
+ if item.Done(kind) {
+ // If it's a noop, we can skip this task
+ delete(taskPool, header.Hash())
+ taskQueue.PopItem()
+ proc = proc - 1
+ progress = true
+ continue
+ }
+ // Remove it from the task queue
+ taskQueue.PopItem()
+ // Otherwise unless the peer is known not to have the data, add to the retrieve list
+ if p.Lacks(header.Hash()) {
+ skip = append(skip, header)
+ } else {
+ send = append(send, header)
+ }
+ }
+ // Merge all the skipped headers back
+ for _, header := range skip {
+ taskQueue.Push(header, -int64(header.Number.Uint64()))
+ }
+ if q.resultCache.HasCompletedItems() {
+ // Wake Results, resultCache was modified
+ q.active.Signal()
+ }
+ // Assemble and return the block download request
+ if len(send) == 0 {
+ return nil, progress, throttled
+ }
+ request := &fetchRequest{
+ Peer: p,
+ Headers: send,
+ Time: time.Now(),
+ }
+ pendPool[p.id] = request
+ return request, progress, throttled
+}
+
+// CancelHeaders aborts a fetch request, returning all pending skeleton indexes to the queue.
+func (q *queue) CancelHeaders(request *fetchRequest) {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+ q.cancel(request, q.headerTaskQueue, q.headerPendPool)
+}
+
+// CancelBodies aborts a body fetch request, returning all pending headers to the
+// task queue.
+func (q *queue) CancelBodies(request *fetchRequest) {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+ q.cancel(request, q.blockTaskQueue, q.blockPendPool)
+}
+
+// CancelReceipts aborts a body fetch request, returning all pending headers to
+// the task queue.
+func (q *queue) CancelReceipts(request *fetchRequest) {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+ q.cancel(request, q.receiptTaskQueue, q.receiptPendPool)
+}
+
+// Cancel aborts a fetch request, returning all pending hashes to the task queue.
+func (q *queue) cancel(request *fetchRequest, taskQueue *prque.Prque, pendPool map[string]*fetchRequest) {
+ if request.From > 0 {
+ taskQueue.Push(request.From, -int64(request.From))
+ }
+ for _, header := range request.Headers {
+ taskQueue.Push(header, -int64(header.Number.Uint64()))
+ }
+ delete(pendPool, request.Peer.id)
+}
+
+// Revoke cancels all pending requests belonging to a given peer. This method is
+// meant to be called during a peer drop to quickly reassign owned data fetches
+// to remaining nodes.
+func (q *queue) Revoke(peerID string) {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ if request, ok := q.blockPendPool[peerID]; ok {
+ for _, header := range request.Headers {
+ q.blockTaskQueue.Push(header, -int64(header.Number.Uint64()))
+ }
+ delete(q.blockPendPool, peerID)
+ }
+ if request, ok := q.receiptPendPool[peerID]; ok {
+ for _, header := range request.Headers {
+ q.receiptTaskQueue.Push(header, -int64(header.Number.Uint64()))
+ }
+ delete(q.receiptPendPool, peerID)
+ }
+}
+
+// ExpireHeaders checks for in flight requests that exceeded a timeout allowance,
+// canceling them and returning the responsible peers for penalisation.
+func (q *queue) ExpireHeaders(timeout time.Duration) map[string]int {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ return q.expire(timeout, q.headerPendPool, q.headerTaskQueue, headerTimeoutMeter)
+}
+
+// ExpireBodies checks for in flight block body requests that exceeded a timeout
+// allowance, canceling them and returning the responsible peers for penalisation.
+func (q *queue) ExpireBodies(timeout time.Duration) map[string]int {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ return q.expire(timeout, q.blockPendPool, q.blockTaskQueue, bodyTimeoutMeter)
+}
+
+// ExpireReceipts checks for in flight receipt requests that exceeded a timeout
+// allowance, canceling them and returning the responsible peers for penalisation.
+func (q *queue) ExpireReceipts(timeout time.Duration) map[string]int {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ return q.expire(timeout, q.receiptPendPool, q.receiptTaskQueue, receiptTimeoutMeter)
+}
+
+// expire is the generic check that move expired tasks from a pending pool back
+// into a task pool, returning all entities caught with expired tasks.
+//
+// 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 {
+ // Iterate over the expired requests and return each to the queue
+ expiries := make(map[string]int)
+ for id, request := range pendPool {
+ if time.Since(request.Time) > timeout {
+ // Update the metrics with the timeout
+ timeoutMeter.Mark(1)
+
+ // Return any non satisfied requests to the pool
+ if request.From > 0 {
+ taskQueue.Push(request.From, -int64(request.From))
+ }
+ for _, header := range request.Headers {
+ taskQueue.Push(header, -int64(header.Number.Uint64()))
+ }
+ // Add the peer to the expiry report along the number of failed requests
+ expiries[id] = len(request.Headers)
+
+ // Remove the expired requests from the pending pool directly
+ delete(pendPool, id)
+ }
+ }
+ return expiries
+}
+
+// DeliverHeaders injects a header retrieval response into the header results
+// cache. This method either accepts all headers it received, or none of them
+// if they do not map correctly to the skeleton.
+//
+// If the headers are accepted, the method makes an attempt to deliver the set
+// of ready headers to the processor to keep the pipeline full. However it will
+// not block to prevent stalling other pending deliveries.
+func (q *queue) DeliverHeaders(id string, headers []*types.Header, headerProcCh chan []*types.Header) (int, error) {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ var logger log.Logger
+ if len(id) < 16 {
+ // Tests use short IDs, don't choke on them
+ logger = log.New("peer", id)
+ } else {
+ logger = log.New("peer", id[:16])
+ }
+ // Short circuit if the data was never requested
+ request := q.headerPendPool[id]
+ if request == nil {
+ return 0, errNoFetchesPending
+ }
+ headerReqTimer.UpdateSince(request.Time)
+ delete(q.headerPendPool, id)
+
+ // Ensure headers can be mapped onto the skeleton chain
+ target := q.headerTaskPool[request.From].Hash()
+
+ accepted := len(headers) == MaxHeaderFetch
+ if accepted {
+ if headers[0].Number.Uint64() != request.From {
+ logger.Trace("First header broke chain ordering", "number", headers[0].Number, "hash", headers[0].Hash(), "expected", request.From)
+ accepted = false
+ } else if headers[len(headers)-1].Hash() != target {
+ logger.Trace("Last header broke skeleton structure ", "number", headers[len(headers)-1].Number, "hash", headers[len(headers)-1].Hash(), "expected", target)
+ accepted = false
+ }
+ }
+ if accepted {
+ parentHash := headers[0].Hash()
+ for i, header := range headers[1:] {
+ hash := header.Hash()
+ if want := request.From + 1 + uint64(i); header.Number.Uint64() != want {
+ logger.Warn("Header broke chain ordering", "number", header.Number, "hash", hash, "expected", want)
+ accepted = false
+ break
+ }
+ if parentHash != header.ParentHash {
+ logger.Warn("Header broke chain ancestry", "number", header.Number, "hash", hash)
+ accepted = false
+ break
+ }
+ // Set-up parent hash for next round
+ parentHash = hash
+ }
+ }
+ // If the batch of headers wasn't accepted, mark as unavailable
+ if !accepted {
+ logger.Trace("Skeleton filling not accepted", "from", request.From)
+
+ miss := q.headerPeerMiss[id]
+ if miss == nil {
+ q.headerPeerMiss[id] = make(map[uint64]struct{})
+ miss = q.headerPeerMiss[id]
+ }
+ miss[request.From] = struct{}{}
+
+ q.headerTaskQueue.Push(request.From, -int64(request.From))
+ return 0, errors.New("delivery not accepted")
+ }
+ // Clean up a successful fetch and try to deliver any sub-results
+ copy(q.headerResults[request.From-q.headerOffset:], headers)
+ delete(q.headerTaskPool, request.From)
+
+ ready := 0
+ for q.headerProced+ready < len(q.headerResults) && q.headerResults[q.headerProced+ready] != nil {
+ ready += MaxHeaderFetch
+ }
+ if ready > 0 {
+ // Headers are ready for delivery, gather them and push forward (non blocking)
+ process := make([]*types.Header, ready)
+ copy(process, q.headerResults[q.headerProced:q.headerProced+ready])
+
+ select {
+ case headerProcCh <- process:
+ logger.Trace("Pre-scheduled new headers", "count", len(process), "from", process[0].Number)
+ q.headerProced += len(process)
+ default:
+ }
+ }
+ // Check for termination and return
+ if len(q.headerTaskPool) == 0 {
+ q.headerContCh <- false
+ }
+ return len(headers), nil
+}
+
+// DeliverBodies injects a block body retrieval response into the results queue.
+// The method returns the number of blocks bodies accepted from the delivery and
+// also wakes any threads waiting for data delivery.
+func (q *queue) DeliverBodies(id string, txLists [][]*types.Transaction, uncleLists [][]*types.Header) (int, error) {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+ trieHasher := trie.NewStackTrie(nil)
+ validate := func(index int, header *types.Header) error {
+ if types.DeriveSha(types.Transactions(txLists[index]), trieHasher) != header.TxHash {
+ return errInvalidBody
+ }
+ if types.CalcUncleHash(uncleLists[index]) != header.UncleHash {
+ return errInvalidBody
+ }
+ return nil
+ }
+
+ reconstruct := func(index int, result *fetchResult) {
+ result.Transactions = txLists[index]
+ result.Uncles = uncleLists[index]
+ result.SetBodyDone()
+ }
+ return q.deliver(id, q.blockTaskPool, q.blockTaskQueue, q.blockPendPool,
+ bodyReqTimer, len(txLists), validate, reconstruct)
+}
+
+// DeliverReceipts injects a receipt retrieval response into the results queue.
+// The method returns the number of transaction receipts accepted from the delivery
+// and also wakes any threads waiting for data delivery.
+func (q *queue) DeliverReceipts(id string, receiptList [][]*types.Receipt) (int, error) {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+ trieHasher := trie.NewStackTrie(nil)
+ validate := func(index int, header *types.Header) error {
+ if types.DeriveSha(types.Receipts(receiptList[index]), trieHasher) != header.ReceiptHash {
+ return errInvalidReceipt
+ }
+ return nil
+ }
+ reconstruct := func(index int, result *fetchResult) {
+ result.Receipts = receiptList[index]
+ result.SetReceiptsDone()
+ }
+ return q.deliver(id, q.receiptTaskPool, q.receiptTaskQueue, q.receiptPendPool,
+ receiptReqTimer, len(receiptList), validate, reconstruct)
+}
+
+// deliver injects a data retrieval response into the results queue.
+//
+// Note, this method expects the queue lock to be already held for writing. The
+// reason this 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, reqTimer metrics.Timer,
+ results int, validate func(index int, header *types.Header) error,
+ reconstruct func(index int, result *fetchResult)) (int, error) {
+
+ // Short circuit if the data was never requested
+ request := pendPool[id]
+ if request == nil {
+ return 0, errNoFetchesPending
+ }
+ reqTimer.UpdateSince(request.Time)
+ delete(pendPool, id)
+
+ // If no data items were retrieved, mark them as unavailable for the origin peer
+ if results == 0 {
+ for _, header := range request.Headers {
+ request.Peer.MarkLacking(header.Hash())
+ }
+ }
+ // Assemble each of the results with their headers and retrieved data parts
+ var (
+ accepted int
+ failure error
+ i int
+ hashes []common.Hash
+ )
+ for _, header := range request.Headers {
+ // Short circuit assembly if no more fetch results are found
+ if i >= results {
+ break
+ }
+ // Validate the fields
+ if err := validate(i, header); err != nil {
+ failure = err
+ break
+ }
+ hashes = append(hashes, header.Hash())
+ i++
+ }
+
+ for _, header := range request.Headers[:i] {
+ if res, stale, err := q.resultCache.GetDeliverySlot(header.Number.Uint64()); err == nil {
+ reconstruct(accepted, res)
+ } else {
+ // else: betweeen here and above, some other peer filled this result,
+ // or it was indeed a no-op. This should not happen, but if it does it's
+ // not something to panic about
+ log.Error("Delivery stale", "stale", stale, "number", header.Number.Uint64(), "err", err)
+ failure = errStaleDelivery
+ }
+ // Clean up a successful fetch
+ delete(taskPool, hashes[accepted])
+ accepted++
+ }
+ // Return all failed or missing fetches to the queue
+ for _, header := range request.Headers[accepted:] {
+ taskQueue.Push(header, -int64(header.Number.Uint64()))
+ }
+ // Wake up Results
+ if accepted > 0 {
+ q.active.Signal()
+ }
+ if failure == nil {
+ return accepted, nil
+ }
+ // If none of the data was good, it's a stale delivery
+ if accepted > 0 {
+ return accepted, fmt.Errorf("partial failure: %v", failure)
+ }
+ return accepted, fmt.Errorf("%w: %v", failure, errStaleDelivery)
+}
+
+// Prepare configures the result cache to allow accepting and caching inbound
+// fetch results.
+func (q *queue) Prepare(offset uint64, mode SyncMode) {
+ q.lock.Lock()
+ defer q.lock.Unlock()
+
+ // Prepare the queue for sync results
+ q.resultCache.Prepare(offset)
+ q.mode = mode
+}
diff --git a/les/downloader/queue_test.go b/les/downloader/queue_test.go
new file mode 100644
index 0000000000..cde5f306a2
--- /dev/null
+++ b/les/downloader/queue_test.go
@@ -0,0 +1,452 @@
+// 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 downloader
+
+import (
+ "fmt"
+ "math/big"
+ "math/rand"
+ "sync"
+ "testing"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus/ethash"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+var (
+ testdb = rawdb.NewMemoryDatabase()
+ genesis = core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000000000))
+)
+
+// makeChain creates a chain of n blocks starting at and including parent.
+// the returned hash chain is ordered head->parent. In addition, every 3rd block
+// contains a transaction and every 5th an uncle to allow testing correct block
+// reassembly.
+func makeChain(n int, seed byte, parent *types.Block, empty bool) ([]*types.Block, []types.Receipts) {
+ blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testdb, n, func(i int, block *core.BlockGen) {
+ block.SetCoinbase(common.Address{seed})
+ // Add one tx to every secondblock
+ if !empty && i%2 == 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, block.BaseFee(), nil), signer, testKey)
+ if err != nil {
+ panic(err)
+ }
+ block.AddTx(tx)
+ }
+ })
+ return blocks, receipts
+}
+
+type chainData struct {
+ blocks []*types.Block
+ offset int
+}
+
+var chain *chainData
+var emptyChain *chainData
+
+func init() {
+ // Create a chain of blocks to import
+ targetBlocks := 128
+ blocks, _ := makeChain(targetBlocks, 0, genesis, false)
+ chain = &chainData{blocks, 0}
+
+ blocks, _ = makeChain(targetBlocks, 0, genesis, true)
+ emptyChain = &chainData{blocks, 0}
+}
+
+func (chain *chainData) headers() []*types.Header {
+ hdrs := make([]*types.Header, len(chain.blocks))
+ for i, b := range chain.blocks {
+ hdrs[i] = b.Header()
+ }
+ return hdrs
+}
+
+func (chain *chainData) Len() int {
+ return len(chain.blocks)
+}
+
+func dummyPeer(id string) *peerConnection {
+ p := &peerConnection{
+ id: id,
+ lacking: make(map[common.Hash]struct{}),
+ }
+ return p
+}
+
+func TestBasics(t *testing.T) {
+ numOfBlocks := len(emptyChain.blocks)
+ numOfReceipts := len(emptyChain.blocks) / 2
+
+ q := newQueue(10, 10)
+ if !q.Idle() {
+ t.Errorf("new queue should be idle")
+ }
+ q.Prepare(1, FastSync)
+ if res := q.Results(false); len(res) != 0 {
+ t.Fatal("new queue should have 0 results")
+ }
+
+ // Schedule a batch of headers
+ q.Schedule(chain.headers(), 1)
+ if q.Idle() {
+ t.Errorf("queue should not be idle")
+ }
+ if got, exp := q.PendingBlocks(), chain.Len(); got != exp {
+ t.Errorf("wrong pending block count, got %d, exp %d", got, exp)
+ }
+ // Only non-empty receipts get added to task-queue
+ if got, exp := q.PendingReceipts(), 64; got != exp {
+ t.Errorf("wrong pending receipt count, got %d, exp %d", got, exp)
+ }
+ // Items are now queued for downloading, next step is that we tell the
+ // queue that a certain peer will deliver them for us
+ {
+ peer := dummyPeer("peer-1")
+ fetchReq, _, throttle := q.ReserveBodies(peer, 50)
+ if !throttle {
+ // queue size is only 10, so throttling should occur
+ t.Fatal("should throttle")
+ }
+ // But we should still get the first things to fetch
+ if got, exp := len(fetchReq.Headers), 5; got != exp {
+ t.Fatalf("expected %d requests, got %d", exp, got)
+ }
+ if got, exp := fetchReq.Headers[0].Number.Uint64(), uint64(1); got != exp {
+ t.Fatalf("expected header %d, got %d", exp, got)
+ }
+ }
+ if exp, got := q.blockTaskQueue.Size(), numOfBlocks-10; exp != got {
+ t.Errorf("expected block task queue to be %d, got %d", exp, got)
+ }
+ if exp, got := q.receiptTaskQueue.Size(), numOfReceipts; exp != got {
+ t.Errorf("expected receipt task queue to be %d, got %d", exp, got)
+ }
+ {
+ peer := dummyPeer("peer-2")
+ fetchReq, _, throttle := q.ReserveBodies(peer, 50)
+
+ // The second peer should hit throttling
+ if !throttle {
+ t.Fatalf("should not throttle")
+ }
+ // And not get any fetches at all, since it was throttled to begin with
+ if fetchReq != nil {
+ t.Fatalf("should have no fetches, got %d", len(fetchReq.Headers))
+ }
+ }
+ if exp, got := q.blockTaskQueue.Size(), numOfBlocks-10; exp != got {
+ t.Errorf("expected block task queue to be %d, got %d", exp, got)
+ }
+ if exp, got := q.receiptTaskQueue.Size(), numOfReceipts; exp != got {
+ t.Errorf("expected receipt task queue to be %d, got %d", exp, got)
+ }
+ {
+ // The receipt delivering peer should not be affected
+ // by the throttling of body deliveries
+ peer := dummyPeer("peer-3")
+ fetchReq, _, throttle := q.ReserveReceipts(peer, 50)
+ if !throttle {
+ // queue size is only 10, so throttling should occur
+ t.Fatal("should throttle")
+ }
+ // But we should still get the first things to fetch
+ if got, exp := len(fetchReq.Headers), 5; got != exp {
+ t.Fatalf("expected %d requests, got %d", exp, got)
+ }
+ if got, exp := fetchReq.Headers[0].Number.Uint64(), uint64(1); got != exp {
+ t.Fatalf("expected header %d, got %d", exp, got)
+ }
+
+ }
+ if exp, got := q.blockTaskQueue.Size(), numOfBlocks-10; exp != got {
+ t.Errorf("expected block task queue to be %d, got %d", exp, got)
+ }
+ if exp, got := q.receiptTaskQueue.Size(), numOfReceipts-5; exp != got {
+ t.Errorf("expected receipt task queue to be %d, got %d", exp, got)
+ }
+ if got, exp := q.resultCache.countCompleted(), 0; got != exp {
+ t.Errorf("wrong processable count, got %d, exp %d", got, exp)
+ }
+}
+
+func TestEmptyBlocks(t *testing.T) {
+ numOfBlocks := len(emptyChain.blocks)
+
+ q := newQueue(10, 10)
+
+ q.Prepare(1, FastSync)
+ // Schedule a batch of headers
+ q.Schedule(emptyChain.headers(), 1)
+ if q.Idle() {
+ t.Errorf("queue should not be idle")
+ }
+ if got, exp := q.PendingBlocks(), len(emptyChain.blocks); got != exp {
+ t.Errorf("wrong pending block count, got %d, exp %d", got, exp)
+ }
+ if got, exp := q.PendingReceipts(), 0; got != exp {
+ t.Errorf("wrong pending receipt count, got %d, exp %d", got, exp)
+ }
+ // They won't be processable, because the fetchresults haven't been
+ // created yet
+ if got, exp := q.resultCache.countCompleted(), 0; got != exp {
+ t.Errorf("wrong processable count, got %d, exp %d", got, exp)
+ }
+
+ // Items are now queued for downloading, next step is that we tell the
+ // queue that a certain peer will deliver them for us
+ // That should trigger all of them to suddenly become 'done'
+ {
+ // Reserve blocks
+ peer := dummyPeer("peer-1")
+ fetchReq, _, _ := q.ReserveBodies(peer, 50)
+
+ // there should be nothing to fetch, blocks are empty
+ if fetchReq != nil {
+ t.Fatal("there should be no body fetch tasks remaining")
+ }
+
+ }
+ if q.blockTaskQueue.Size() != numOfBlocks-10 {
+ t.Errorf("expected block task queue to be %d, got %d", numOfBlocks-10, q.blockTaskQueue.Size())
+ }
+ if q.receiptTaskQueue.Size() != 0 {
+ t.Errorf("expected receipt task queue to be %d, got %d", 0, q.receiptTaskQueue.Size())
+ }
+ {
+ peer := dummyPeer("peer-3")
+ fetchReq, _, _ := q.ReserveReceipts(peer, 50)
+
+ // there should be nothing to fetch, blocks are empty
+ if fetchReq != nil {
+ t.Fatal("there should be no body fetch tasks remaining")
+ }
+ }
+ if q.blockTaskQueue.Size() != numOfBlocks-10 {
+ t.Errorf("expected block task queue to be %d, got %d", numOfBlocks-10, q.blockTaskQueue.Size())
+ }
+ if q.receiptTaskQueue.Size() != 0 {
+ t.Errorf("expected receipt task queue to be %d, got %d", 0, q.receiptTaskQueue.Size())
+ }
+ if got, exp := q.resultCache.countCompleted(), 10; got != exp {
+ t.Errorf("wrong processable count, got %d, exp %d", got, exp)
+ }
+}
+
+// XTestDelivery does some more extensive testing of events that happen,
+// blocks that become known and peers that make reservations and deliveries.
+// disabled since it's not really a unit-test, but can be executed to test
+// some more advanced scenarios
+func XTestDelivery(t *testing.T) {
+ // the outside network, holding blocks
+ blo, rec := makeChain(128, 0, genesis, false)
+ world := newNetwork()
+ world.receipts = rec
+ world.chain = blo
+ world.progress(10)
+ if false {
+ log.Root().SetHandler(log.StdoutHandler)
+
+ }
+ q := newQueue(10, 10)
+ var wg sync.WaitGroup
+ q.Prepare(1, FastSync)
+ wg.Add(1)
+ go func() {
+ // deliver headers
+ defer wg.Done()
+ c := 1
+ for {
+ //fmt.Printf("getting headers from %d\n", c)
+ hdrs := world.headers(c)
+ l := len(hdrs)
+ //fmt.Printf("scheduling %d headers, first %d last %d\n",
+ // l, hdrs[0].Number.Uint64(), hdrs[len(hdrs)-1].Number.Uint64())
+ q.Schedule(hdrs, uint64(c))
+ c += l
+ }
+ }()
+ wg.Add(1)
+ go func() {
+ // collect results
+ defer wg.Done()
+ tot := 0
+ for {
+ res := q.Results(true)
+ tot += len(res)
+ fmt.Printf("got %d results, %d tot\n", len(res), tot)
+ // Now we can forget about these
+ world.forget(res[len(res)-1].Header.Number.Uint64())
+
+ }
+ }()
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ // reserve body fetch
+ i := 4
+ for {
+ peer := dummyPeer(fmt.Sprintf("peer-%d", i))
+ f, _, _ := q.ReserveBodies(peer, rand.Intn(30))
+ if f != nil {
+ var emptyList []*types.Header
+ var txs [][]*types.Transaction
+ var uncles [][]*types.Header
+ numToSkip := rand.Intn(len(f.Headers))
+ for _, hdr := range f.Headers[0 : len(f.Headers)-numToSkip] {
+ txs = append(txs, world.getTransactions(hdr.Number.Uint64()))
+ uncles = append(uncles, emptyList)
+ }
+ time.Sleep(100 * time.Millisecond)
+ _, err := q.DeliverBodies(peer.id, txs, uncles)
+ if err != nil {
+ fmt.Printf("delivered %d bodies %v\n", len(txs), err)
+ }
+ } else {
+ i++
+ time.Sleep(200 * time.Millisecond)
+ }
+ }
+ }()
+ go func() {
+ defer wg.Done()
+ // reserve receiptfetch
+ peer := dummyPeer("peer-3")
+ for {
+ f, _, _ := q.ReserveReceipts(peer, rand.Intn(50))
+ if f != nil {
+ var rcs [][]*types.Receipt
+ for _, hdr := range f.Headers {
+ rcs = append(rcs, world.getReceipts(hdr.Number.Uint64()))
+ }
+ _, err := q.DeliverReceipts(peer.id, rcs)
+ if err != nil {
+ fmt.Printf("delivered %d receipts %v\n", len(rcs), err)
+ }
+ time.Sleep(100 * time.Millisecond)
+ } else {
+ time.Sleep(200 * time.Millisecond)
+ }
+ }
+ }()
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for i := 0; i < 50; i++ {
+ time.Sleep(300 * time.Millisecond)
+ //world.tick()
+ //fmt.Printf("trying to progress\n")
+ world.progress(rand.Intn(100))
+ }
+ for i := 0; i < 50; i++ {
+ time.Sleep(2990 * time.Millisecond)
+
+ }
+ }()
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ for {
+ time.Sleep(990 * time.Millisecond)
+ fmt.Printf("world block tip is %d\n",
+ world.chain[len(world.chain)-1].Header().Number.Uint64())
+ fmt.Println(q.Stats())
+ }
+ }()
+ wg.Wait()
+}
+
+func newNetwork() *network {
+ var l sync.RWMutex
+ return &network{
+ cond: sync.NewCond(&l),
+ offset: 1, // block 1 is at blocks[0]
+ }
+}
+
+// represents the network
+type network struct {
+ offset int
+ chain []*types.Block
+ receipts []types.Receipts
+ lock sync.RWMutex
+ cond *sync.Cond
+}
+
+func (n *network) getTransactions(blocknum uint64) types.Transactions {
+ index := blocknum - uint64(n.offset)
+ return n.chain[index].Transactions()
+}
+func (n *network) getReceipts(blocknum uint64) types.Receipts {
+ index := blocknum - uint64(n.offset)
+ if got := n.chain[index].Header().Number.Uint64(); got != blocknum {
+ fmt.Printf("Err, got %d exp %d\n", got, blocknum)
+ panic("sd")
+ }
+ return n.receipts[index]
+}
+
+func (n *network) forget(blocknum uint64) {
+ index := blocknum - uint64(n.offset)
+ n.chain = n.chain[index:]
+ n.receipts = n.receipts[index:]
+ n.offset = int(blocknum)
+
+}
+func (n *network) progress(numBlocks int) {
+
+ n.lock.Lock()
+ defer n.lock.Unlock()
+ //fmt.Printf("progressing...\n")
+ newBlocks, newR := makeChain(numBlocks, 0, n.chain[len(n.chain)-1], false)
+ n.chain = append(n.chain, newBlocks...)
+ n.receipts = append(n.receipts, newR...)
+ n.cond.Broadcast()
+
+}
+
+func (n *network) headers(from int) []*types.Header {
+ numHeaders := 128
+ var hdrs []*types.Header
+ index := from - n.offset
+
+ for index >= len(n.chain) {
+ // wait for progress
+ n.cond.L.Lock()
+ //fmt.Printf("header going into wait\n")
+ n.cond.Wait()
+ index = from - n.offset
+ n.cond.L.Unlock()
+ }
+ n.lock.RLock()
+ defer n.lock.RUnlock()
+ for i, b := range n.chain[index:] {
+ hdrs = append(hdrs, b.Header())
+ if i >= numHeaders {
+ break
+ }
+ }
+ return hdrs
+}
diff --git a/les/downloader/resultstore.go b/les/downloader/resultstore.go
new file mode 100644
index 0000000000..21928c2a00
--- /dev/null
+++ b/les/downloader/resultstore.go
@@ -0,0 +1,194 @@
+// 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 downloader
+
+import (
+ "fmt"
+ "sync"
+ "sync/atomic"
+
+ "github.com/ethereum/go-ethereum/core/types"
+)
+
+// resultStore implements a structure for maintaining fetchResults, tracking their
+// download-progress and delivering (finished) results.
+type resultStore struct {
+ items []*fetchResult // Downloaded but not yet delivered fetch results
+ resultOffset uint64 // Offset of the first cached fetch result in the block chain
+
+ // Internal index of first non-completed entry, updated atomically when needed.
+ // If all items are complete, this will equal length(items), so
+ // *important* : is not safe to use for indexing without checking against length
+ indexIncomplete int32 // atomic access
+
+ // throttleThreshold is the limit up to which we _want_ to fill the
+ // results. If blocks are large, we want to limit the results to less
+ // than the number of available slots, and maybe only fill 1024 out of
+ // 8192 possible places. The queue will, at certain times, recalibrate
+ // this index.
+ throttleThreshold uint64
+
+ lock sync.RWMutex
+}
+
+func newResultStore(size int) *resultStore {
+ return &resultStore{
+ resultOffset: 0,
+ items: make([]*fetchResult, size),
+ throttleThreshold: uint64(size),
+ }
+}
+
+// SetThrottleThreshold updates the throttling threshold based on the requested
+// limit and the total queue capacity. It returns the (possibly capped) threshold
+func (r *resultStore) SetThrottleThreshold(threshold uint64) uint64 {
+ r.lock.Lock()
+ defer r.lock.Unlock()
+
+ limit := uint64(len(r.items))
+ if threshold >= limit {
+ threshold = limit
+ }
+ r.throttleThreshold = threshold
+ return r.throttleThreshold
+}
+
+// AddFetch adds a header for body/receipt fetching. This is used when the queue
+// wants to reserve headers for fetching.
+//
+// It returns the following:
+// stale - if true, this item is already passed, and should not be requested again
+// throttled - if true, the store is at capacity, this particular header is not prio now
+// item - the result to store data into
+// err - any error that occurred
+func (r *resultStore) AddFetch(header *types.Header, fastSync bool) (stale, throttled bool, item *fetchResult, err error) {
+ r.lock.Lock()
+ defer r.lock.Unlock()
+
+ var index int
+ item, index, stale, throttled, err = r.getFetchResult(header.Number.Uint64())
+ if err != nil || stale || throttled {
+ return stale, throttled, item, err
+ }
+ if item == nil {
+ item = newFetchResult(header, fastSync)
+ r.items[index] = item
+ }
+ return stale, throttled, item, err
+}
+
+// GetDeliverySlot returns the fetchResult for the given header. If the 'stale' flag
+// is true, that means the header has already been delivered 'upstream'. This method
+// does not bubble up the 'throttle' flag, since it's moot at the point in time when
+// the item is downloaded and ready for delivery
+func (r *resultStore) GetDeliverySlot(headerNumber uint64) (*fetchResult, bool, error) {
+ r.lock.RLock()
+ defer r.lock.RUnlock()
+
+ res, _, stale, _, err := r.getFetchResult(headerNumber)
+ return res, stale, err
+}
+
+// getFetchResult returns the fetchResult corresponding to the given item, and
+// the index where the result is stored.
+func (r *resultStore) getFetchResult(headerNumber uint64) (item *fetchResult, index int, stale, throttle bool, err error) {
+ index = int(int64(headerNumber) - int64(r.resultOffset))
+ throttle = index >= int(r.throttleThreshold)
+ stale = index < 0
+
+ if index >= len(r.items) {
+ err = fmt.Errorf("%w: index allocation went beyond available resultStore space "+
+ "(index [%d] = header [%d] - resultOffset [%d], len(resultStore) = %d", errInvalidChain,
+ index, headerNumber, r.resultOffset, len(r.items))
+ return nil, index, stale, throttle, err
+ }
+ if stale {
+ return nil, index, stale, throttle, nil
+ }
+ item = r.items[index]
+ return item, index, stale, throttle, nil
+}
+
+// hasCompletedItems returns true if there are processable items available
+// this method is cheaper than countCompleted
+func (r *resultStore) HasCompletedItems() bool {
+ r.lock.RLock()
+ defer r.lock.RUnlock()
+
+ if len(r.items) == 0 {
+ return false
+ }
+ if item := r.items[0]; item != nil && item.AllDone() {
+ return true
+ }
+ return false
+}
+
+// countCompleted returns the number of items ready for delivery, stopping at
+// the first non-complete item.
+//
+// The mthod assumes (at least) rlock is held.
+func (r *resultStore) countCompleted() int {
+ // We iterate from the already known complete point, and see
+ // if any more has completed since last count
+ index := atomic.LoadInt32(&r.indexIncomplete)
+ for ; ; index++ {
+ if index >= int32(len(r.items)) {
+ break
+ }
+ result := r.items[index]
+ if result == nil || !result.AllDone() {
+ break
+ }
+ }
+ atomic.StoreInt32(&r.indexIncomplete, index)
+ return int(index)
+}
+
+// GetCompleted returns the next batch of completed fetchResults
+func (r *resultStore) GetCompleted(limit int) []*fetchResult {
+ r.lock.Lock()
+ defer r.lock.Unlock()
+
+ completed := r.countCompleted()
+ if limit > completed {
+ limit = completed
+ }
+ results := make([]*fetchResult, limit)
+ copy(results, r.items[:limit])
+
+ // Delete the results from the cache and clear the tail.
+ copy(r.items, r.items[limit:])
+ for i := len(r.items) - limit; i < len(r.items); i++ {
+ r.items[i] = nil
+ }
+ // Advance the expected block number of the first cache entry
+ r.resultOffset += uint64(limit)
+ atomic.AddInt32(&r.indexIncomplete, int32(-limit))
+
+ return results
+}
+
+// Prepare initialises the offset with the given block number
+func (r *resultStore) Prepare(offset uint64) {
+ r.lock.Lock()
+ defer r.lock.Unlock()
+
+ if r.resultOffset < offset {
+ r.resultOffset = offset
+ }
+}
diff --git a/les/downloader/statesync.go b/les/downloader/statesync.go
new file mode 100644
index 0000000000..6c53e5577a
--- /dev/null
+++ b/les/downloader/statesync.go
@@ -0,0 +1,615 @@
+// 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 downloader
+
+import (
+ "fmt"
+ "sync"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/state"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/trie"
+ "golang.org/x/crypto/sha3"
+)
+
+// stateReq represents a batch of state fetch requests grouped together into
+// a single data retrieval network packet.
+type stateReq struct {
+ nItems uint16 // Number of items requested for download (max is 384, so uint16 is sufficient)
+ trieTasks map[common.Hash]*trieTask // Trie node download tasks to track previous attempts
+ codeTasks map[common.Hash]*codeTask // Byte code download tasks to track previous attempts
+ timeout time.Duration // Maximum round trip time for this to complete
+ timer *time.Timer // Timer to fire when the RTT timeout expires
+ peer *peerConnection // Peer that we're requesting from
+ delivered time.Time // Time when the packet was delivered (independent when we process it)
+ response [][]byte // Response data of the peer (nil for timeouts)
+ dropped bool // Flag whether the peer dropped off early
+}
+
+// timedOut returns if this request timed out.
+func (req *stateReq) timedOut() bool {
+ return req.response == nil
+}
+
+// stateSyncStats is a collection of progress stats to report during a state trie
+// sync to RPC requests as well as to display in user logs.
+type stateSyncStats struct {
+ processed uint64 // Number of state entries processed
+ duplicate uint64 // Number of state entries downloaded twice
+ unexpected uint64 // Number of non-requested state entries received
+ pending uint64 // Number of still pending state entries
+}
+
+// syncState starts downloading state with the given root hash.
+func (d *Downloader) syncState(root common.Hash) *stateSync {
+ // Create the state sync
+ s := newStateSync(d, root)
+ select {
+ case d.stateSyncStart <- s:
+ // If we tell the statesync to restart with a new root, we also need
+ // to wait for it to actually also start -- when old requests have timed
+ // out or been delivered
+ <-s.started
+ case <-d.quitCh:
+ s.err = errCancelStateFetch
+ close(s.done)
+ }
+ return s
+}
+
+// stateFetcher manages the active state sync and accepts requests
+// on its behalf.
+func (d *Downloader) stateFetcher() {
+ for {
+ select {
+ case s := <-d.stateSyncStart:
+ for next := s; next != nil; {
+ next = d.runStateSync(next)
+ }
+ case <-d.stateCh:
+ // Ignore state responses while no sync is running.
+ case <-d.quitCh:
+ return
+ }
+ }
+}
+
+// runStateSync runs a state synchronisation until it completes or another root
+// hash is requested to be switched over to.
+func (d *Downloader) runStateSync(s *stateSync) *stateSync {
+ var (
+ active = make(map[string]*stateReq) // Currently in-flight requests
+ finished []*stateReq // Completed or failed requests
+ timeout = make(chan *stateReq) // Timed out active requests
+ )
+ log.Trace("State sync starting", "root", s.root)
+
+ defer func() {
+ // Cancel active request timers on exit. Also set peers to idle so they're
+ // available for the next sync.
+ for _, req := range active {
+ req.timer.Stop()
+ req.peer.SetNodeDataIdle(int(req.nItems), time.Now())
+ }
+ }()
+ go s.run()
+ defer s.Cancel()
+
+ // Listen for peer departure events to cancel assigned tasks
+ peerDrop := make(chan *peerConnection, 1024)
+ peerSub := s.d.peers.SubscribePeerDrops(peerDrop)
+ defer peerSub.Unsubscribe()
+
+ for {
+ // Enable sending of the first buffered element if there is one.
+ var (
+ deliverReq *stateReq
+ deliverReqCh chan *stateReq
+ )
+ if len(finished) > 0 {
+ deliverReq = finished[0]
+ deliverReqCh = s.deliver
+ }
+
+ select {
+ // The stateSync lifecycle:
+ case next := <-d.stateSyncStart:
+ d.spindownStateSync(active, finished, timeout, peerDrop)
+ return next
+
+ case <-s.done:
+ d.spindownStateSync(active, finished, timeout, peerDrop)
+ return nil
+
+ // Send the next finished request to the current sync:
+ case deliverReqCh <- deliverReq:
+ // Shift out the first request, but also set the emptied slot to nil for GC
+ copy(finished, finished[1:])
+ finished[len(finished)-1] = nil
+ finished = finished[:len(finished)-1]
+
+ // Handle incoming state packs:
+ case pack := <-d.stateCh:
+ // Discard any data not requested (or previously timed out)
+ req := active[pack.PeerId()]
+ if req == nil {
+ log.Debug("Unrequested node data", "peer", pack.PeerId(), "len", pack.Items())
+ continue
+ }
+ // Finalize the request and queue up for processing
+ req.timer.Stop()
+ req.response = pack.(*statePack).states
+ req.delivered = time.Now()
+
+ finished = append(finished, req)
+ delete(active, pack.PeerId())
+
+ // Handle dropped peer connections:
+ case p := <-peerDrop:
+ // Skip if no request is currently pending
+ req := active[p.id]
+ if req == nil {
+ continue
+ }
+ // Finalize the request and queue up for processing
+ req.timer.Stop()
+ req.dropped = true
+ req.delivered = time.Now()
+
+ finished = append(finished, req)
+ delete(active, p.id)
+
+ // Handle timed-out requests:
+ case req := <-timeout:
+ // If the peer is already requesting something else, ignore the stale timeout.
+ // This can happen when the timeout and the delivery happens simultaneously,
+ // causing both pathways to trigger.
+ if active[req.peer.id] != req {
+ continue
+ }
+ req.delivered = time.Now()
+ // Move the timed out data back into the download queue
+ finished = append(finished, req)
+ delete(active, req.peer.id)
+
+ // Track outgoing state requests:
+ case req := <-d.trackStateReq:
+ // If an active request already exists for this peer, we have a problem. In
+ // theory the trie node schedule must never assign two requests to the same
+ // peer. In practice however, a peer might receive a request, disconnect and
+ // immediately reconnect before the previous times out. In this case the first
+ // request is never honored, alas we must not silently overwrite it, as that
+ // causes valid requests to go missing and sync to get stuck.
+ if old := active[req.peer.id]; old != nil {
+ log.Warn("Busy peer assigned new state fetch", "peer", old.peer.id)
+ // Move the previous request to the finished set
+ old.timer.Stop()
+ old.dropped = true
+ old.delivered = time.Now()
+ finished = append(finished, old)
+ }
+ // Start a timer to notify the sync loop if the peer stalled.
+ req.timer = time.AfterFunc(req.timeout, func() {
+ timeout <- req
+ })
+ active[req.peer.id] = req
+ }
+ }
+}
+
+// spindownStateSync 'drains' the outstanding requests; some will be delivered and other
+// will time out. This is to ensure that when the next stateSync starts working, all peers
+// are marked as idle and de facto _are_ idle.
+func (d *Downloader) spindownStateSync(active map[string]*stateReq, finished []*stateReq, timeout chan *stateReq, peerDrop chan *peerConnection) {
+ log.Trace("State sync spinning down", "active", len(active), "finished", len(finished))
+ for len(active) > 0 {
+ var (
+ req *stateReq
+ reason string
+ )
+ select {
+ // Handle (drop) incoming state packs:
+ case pack := <-d.stateCh:
+ req = active[pack.PeerId()]
+ reason = "delivered"
+ // Handle dropped peer connections:
+ case p := <-peerDrop:
+ req = active[p.id]
+ reason = "peerdrop"
+ // Handle timed-out requests:
+ case req = <-timeout:
+ reason = "timeout"
+ }
+ if req == nil {
+ continue
+ }
+ req.peer.log.Trace("State peer marked idle (spindown)", "req.items", int(req.nItems), "reason", reason)
+ req.timer.Stop()
+ delete(active, req.peer.id)
+ req.peer.SetNodeDataIdle(int(req.nItems), time.Now())
+ }
+ // The 'finished' set contains deliveries that we were going to pass to processing.
+ // Those are now moot, but we still need to set those peers as idle, which would
+ // otherwise have been done after processing
+ for _, req := range finished {
+ req.peer.SetNodeDataIdle(int(req.nItems), time.Now())
+ }
+}
+
+// stateSync schedules requests for downloading a particular state trie defined
+// by a given state root.
+type stateSync struct {
+ d *Downloader // Downloader instance to access and manage current peerset
+
+ root common.Hash // State root currently being synced
+ sched *trie.Sync // State trie sync scheduler defining the tasks
+ keccak crypto.KeccakState // Keccak256 hasher to verify deliveries with
+
+ trieTasks map[common.Hash]*trieTask // Set of trie node tasks currently queued for retrieval
+ codeTasks map[common.Hash]*codeTask // Set of byte code tasks currently queued for retrieval
+
+ numUncommitted int
+ bytesUncommitted int
+
+ started chan struct{} // Started is signalled once the sync loop starts
+
+ deliver chan *stateReq // Delivery channel multiplexing peer responses
+ cancel chan struct{} // Channel to signal a termination request
+ cancelOnce sync.Once // Ensures cancel only ever gets called once
+ done chan struct{} // Channel to signal termination completion
+ err error // Any error hit during sync (set before completion)
+}
+
+// trieTask represents a single trie node download task, containing a set of
+// peers already attempted retrieval from to detect stalled syncs and abort.
+type trieTask struct {
+ path [][]byte
+ attempts map[string]struct{}
+}
+
+// codeTask represents a single byte code download task, containing a set of
+// peers already attempted retrieval from to detect stalled syncs and abort.
+type codeTask struct {
+ attempts map[string]struct{}
+}
+
+// newStateSync creates a new state trie download scheduler. This method does not
+// yet start the sync. The user needs to call run to initiate.
+func newStateSync(d *Downloader, root common.Hash) *stateSync {
+ return &stateSync{
+ d: d,
+ root: root,
+ sched: state.NewStateSync(root, d.stateDB, d.stateBloom, nil),
+ keccak: sha3.NewLegacyKeccak256().(crypto.KeccakState),
+ trieTasks: make(map[common.Hash]*trieTask),
+ codeTasks: make(map[common.Hash]*codeTask),
+ deliver: make(chan *stateReq),
+ cancel: make(chan struct{}),
+ done: make(chan struct{}),
+ started: make(chan struct{}),
+ }
+}
+
+// run starts the task assignment and response processing loop, blocking until
+// it finishes, and finally notifying any goroutines waiting for the loop to
+// finish.
+func (s *stateSync) run() {
+ close(s.started)
+ if s.d.snapSync {
+ s.err = s.d.SnapSyncer.Sync(s.root, s.cancel)
+ } else {
+ s.err = s.loop()
+ }
+ close(s.done)
+}
+
+// Wait blocks until the sync is done or canceled.
+func (s *stateSync) Wait() error {
+ <-s.done
+ return s.err
+}
+
+// Cancel cancels the sync and waits until it has shut down.
+func (s *stateSync) Cancel() error {
+ s.cancelOnce.Do(func() {
+ close(s.cancel)
+ })
+ return s.Wait()
+}
+
+// loop is the main event loop of a state trie sync. It it responsible for the
+// assignment of new tasks to peers (including sending it to them) as well as
+// for the processing of inbound data. Note, that the loop does not directly
+// receive data from peers, rather those are buffered up in the downloader and
+// pushed here async. The reason is to decouple processing from data receipt
+// and timeouts.
+func (s *stateSync) loop() (err error) {
+ // Listen for new peer events to assign tasks to them
+ newPeer := make(chan *peerConnection, 1024)
+ peerSub := s.d.peers.SubscribeNewPeers(newPeer)
+ defer peerSub.Unsubscribe()
+ defer func() {
+ cerr := s.commit(true)
+ if err == nil {
+ err = cerr
+ }
+ }()
+
+ // Keep assigning new tasks until the sync completes or aborts
+ for s.sched.Pending() > 0 {
+ if err = s.commit(false); err != nil {
+ return err
+ }
+ s.assignTasks()
+ // Tasks assigned, wait for something to happen
+ select {
+ case <-newPeer:
+ // New peer arrived, try to assign it download tasks
+
+ case <-s.cancel:
+ return errCancelStateFetch
+
+ case <-s.d.cancelCh:
+ return errCanceled
+
+ case req := <-s.deliver:
+ // Response, disconnect or timeout triggered, drop the peer if stalling
+ log.Trace("Received node data response", "peer", req.peer.id, "count", len(req.response), "dropped", req.dropped, "timeout", !req.dropped && req.timedOut())
+ if req.nItems <= 2 && !req.dropped && req.timedOut() {
+ // 2 items are the minimum requested, if even that times out, we've no use of
+ // this peer at the moment.
+ log.Warn("Stalling state sync, dropping peer", "peer", req.peer.id)
+ if s.d.dropPeer == nil {
+ // The dropPeer method is nil when `--copydb` is used for a local copy.
+ // Timeouts can occur if e.g. compaction hits at the wrong time, and can be ignored
+ req.peer.log.Warn("Downloader wants to drop peer, but peerdrop-function is not set", "peer", req.peer.id)
+ } else {
+ s.d.dropPeer(req.peer.id)
+
+ // If this peer was the master peer, abort sync immediately
+ s.d.cancelLock.RLock()
+ master := req.peer.id == s.d.cancelPeer
+ s.d.cancelLock.RUnlock()
+
+ if master {
+ s.d.cancel()
+ return errTimeout
+ }
+ }
+ }
+ // Process all the received blobs and check for stale delivery
+ delivered, err := s.process(req)
+ req.peer.SetNodeDataIdle(delivered, req.delivered)
+ if err != nil {
+ log.Warn("Node data write error", "err", err)
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+func (s *stateSync) commit(force bool) error {
+ if !force && s.bytesUncommitted < ethdb.IdealBatchSize {
+ return nil
+ }
+ start := time.Now()
+ b := s.d.stateDB.NewBatch()
+ if err := s.sched.Commit(b); err != nil {
+ return err
+ }
+ if err := b.Write(); err != nil {
+ return fmt.Errorf("DB write error: %v", err)
+ }
+ s.updateStats(s.numUncommitted, 0, 0, time.Since(start))
+ s.numUncommitted = 0
+ s.bytesUncommitted = 0
+ return nil
+}
+
+// assignTasks attempts to assign new tasks to all idle peers, either from the
+// batch currently being retried, or fetching new data from the trie sync itself.
+func (s *stateSync) assignTasks() {
+ // Iterate over all idle peers and try to assign them state fetches
+ peers, _ := s.d.peers.NodeDataIdlePeers()
+ for _, p := range peers {
+ // Assign a batch of fetches proportional to the estimated latency/bandwidth
+ cap := p.NodeDataCapacity(s.d.peers.rates.TargetRoundTrip())
+ req := &stateReq{peer: p, timeout: s.d.peers.rates.TargetTimeout()}
+
+ nodes, _, codes := s.fillTasks(cap, req)
+
+ // If the peer was assigned tasks to fetch, send the network request
+ if len(nodes)+len(codes) > 0 {
+ req.peer.log.Trace("Requesting batch of state data", "nodes", len(nodes), "codes", len(codes), "root", s.root)
+ select {
+ case s.d.trackStateReq <- req:
+ req.peer.FetchNodeData(append(nodes, codes...)) // Unified retrieval under eth/6x
+ case <-s.cancel:
+ case <-s.d.cancelCh:
+ }
+ }
+ }
+}
+
+// fillTasks fills the given request object with a maximum of n state download
+// tasks to send to the remote peer.
+func (s *stateSync) fillTasks(n int, req *stateReq) (nodes []common.Hash, paths []trie.SyncPath, codes []common.Hash) {
+ // Refill available tasks from the scheduler.
+ if fill := n - (len(s.trieTasks) + len(s.codeTasks)); fill > 0 {
+ nodes, paths, codes := s.sched.Missing(fill)
+ for i, hash := range nodes {
+ s.trieTasks[hash] = &trieTask{
+ path: paths[i],
+ attempts: make(map[string]struct{}),
+ }
+ }
+ for _, hash := range codes {
+ s.codeTasks[hash] = &codeTask{
+ attempts: make(map[string]struct{}),
+ }
+ }
+ }
+ // Find tasks that haven't been tried with the request's peer. Prefer code
+ // over trie nodes as those can be written to disk and forgotten about.
+ nodes = make([]common.Hash, 0, n)
+ paths = make([]trie.SyncPath, 0, n)
+ codes = make([]common.Hash, 0, n)
+
+ req.trieTasks = make(map[common.Hash]*trieTask, n)
+ req.codeTasks = make(map[common.Hash]*codeTask, n)
+
+ for hash, t := range s.codeTasks {
+ // Stop when we've gathered enough requests
+ if len(nodes)+len(codes) == n {
+ break
+ }
+ // Skip any requests we've already tried from this peer
+ if _, ok := t.attempts[req.peer.id]; ok {
+ continue
+ }
+ // Assign the request to this peer
+ t.attempts[req.peer.id] = struct{}{}
+ codes = append(codes, hash)
+ req.codeTasks[hash] = t
+ delete(s.codeTasks, hash)
+ }
+ for hash, t := range s.trieTasks {
+ // Stop when we've gathered enough requests
+ if len(nodes)+len(codes) == n {
+ break
+ }
+ // Skip any requests we've already tried from this peer
+ if _, ok := t.attempts[req.peer.id]; ok {
+ continue
+ }
+ // Assign the request to this peer
+ t.attempts[req.peer.id] = struct{}{}
+
+ nodes = append(nodes, hash)
+ paths = append(paths, t.path)
+
+ req.trieTasks[hash] = t
+ delete(s.trieTasks, hash)
+ }
+ req.nItems = uint16(len(nodes) + len(codes))
+ return nodes, paths, codes
+}
+
+// process iterates over a batch of delivered state data, injecting each item
+// into a running state sync, re-queuing any items that were requested but not
+// delivered. Returns whether the peer actually managed to deliver anything of
+// value, and any error that occurred.
+func (s *stateSync) process(req *stateReq) (int, error) {
+ // Collect processing stats and update progress if valid data was received
+ duplicate, unexpected, successful := 0, 0, 0
+
+ defer func(start time.Time) {
+ if duplicate > 0 || unexpected > 0 {
+ s.updateStats(0, duplicate, unexpected, time.Since(start))
+ }
+ }(time.Now())
+
+ // Iterate over all the delivered data and inject one-by-one into the trie
+ for _, blob := range req.response {
+ hash, err := s.processNodeData(blob)
+ switch err {
+ case nil:
+ s.numUncommitted++
+ s.bytesUncommitted += len(blob)
+ successful++
+ case trie.ErrNotRequested:
+ unexpected++
+ case trie.ErrAlreadyProcessed:
+ duplicate++
+ default:
+ return successful, fmt.Errorf("invalid state node %s: %v", hash.TerminalString(), err)
+ }
+ // Delete from both queues (one delivery is enough for the syncer)
+ delete(req.trieTasks, hash)
+ delete(req.codeTasks, hash)
+ }
+ // Put unfulfilled tasks back into the retry queue
+ npeers := s.d.peers.Len()
+ for hash, task := range req.trieTasks {
+ // If the node did deliver something, missing items may be due to a protocol
+ // limit or a previous timeout + delayed delivery. Both cases should permit
+ // the node to retry the missing items (to avoid single-peer stalls).
+ if len(req.response) > 0 || req.timedOut() {
+ delete(task.attempts, req.peer.id)
+ }
+ // If we've requested the node too many times already, it may be a malicious
+ // sync where nobody has the right data. Abort.
+ if len(task.attempts) >= npeers {
+ return successful, fmt.Errorf("trie node %s failed with all peers (%d tries, %d peers)", hash.TerminalString(), len(task.attempts), npeers)
+ }
+ // Missing item, place into the retry queue.
+ s.trieTasks[hash] = task
+ }
+ for hash, task := range req.codeTasks {
+ // If the node did deliver something, missing items may be due to a protocol
+ // limit or a previous timeout + delayed delivery. Both cases should permit
+ // the node to retry the missing items (to avoid single-peer stalls).
+ if len(req.response) > 0 || req.timedOut() {
+ delete(task.attempts, req.peer.id)
+ }
+ // If we've requested the node too many times already, it may be a malicious
+ // sync where nobody has the right data. Abort.
+ if len(task.attempts) >= npeers {
+ return successful, fmt.Errorf("byte code %s failed with all peers (%d tries, %d peers)", hash.TerminalString(), len(task.attempts), npeers)
+ }
+ // Missing item, place into the retry queue.
+ s.codeTasks[hash] = task
+ }
+ return successful, nil
+}
+
+// processNodeData tries to inject a trie node data blob delivered from a remote
+// peer into the state trie, returning whether anything useful was written or any
+// error occurred.
+func (s *stateSync) processNodeData(blob []byte) (common.Hash, error) {
+ res := trie.SyncResult{Data: blob}
+ s.keccak.Reset()
+ s.keccak.Write(blob)
+ s.keccak.Read(res.Hash[:])
+ err := s.sched.Process(res)
+ return res.Hash, err
+}
+
+// updateStats bumps the various state sync progress counters and displays a log
+// message for the user to see.
+func (s *stateSync) updateStats(written, duplicate, unexpected int, duration time.Duration) {
+ s.d.syncStatsLock.Lock()
+ defer s.d.syncStatsLock.Unlock()
+
+ s.d.syncStatsState.pending = uint64(s.sched.Pending())
+ s.d.syncStatsState.processed += uint64(written)
+ s.d.syncStatsState.duplicate += uint64(duplicate)
+ s.d.syncStatsState.unexpected += uint64(unexpected)
+
+ if written > 0 || duplicate > 0 || unexpected > 0 {
+ log.Info("Imported new state entries", "count", written, "elapsed", common.PrettyDuration(duration), "processed", s.d.syncStatsState.processed, "pending", s.d.syncStatsState.pending, "trieretry", len(s.trieTasks), "coderetry", len(s.codeTasks), "duplicate", s.d.syncStatsState.duplicate, "unexpected", s.d.syncStatsState.unexpected)
+ }
+ if written > 0 {
+ rawdb.WriteFastTrieProgress(s.d.stateDB, s.d.syncStatsState.processed)
+ }
+}
diff --git a/les/downloader/testchain_test.go b/les/downloader/testchain_test.go
new file mode 100644
index 0000000000..b9865f7e03
--- /dev/null
+++ b/les/downloader/testchain_test.go
@@ -0,0 +1,230 @@
+// 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 downloader
+
+import (
+ "fmt"
+ "math/big"
+ "sync"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus/ethash"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/params"
+)
+
+// Test chain parameters.
+var (
+ testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ testAddress = crypto.PubkeyToAddress(testKey.PublicKey)
+ testDB = rawdb.NewMemoryDatabase()
+ testGenesis = core.GenesisBlockForTesting(testDB, testAddress, big.NewInt(1000000000000000))
+)
+
+// The common prefix of all test chains:
+var testChainBase = newTestChain(blockCacheMaxItems+200, testGenesis)
+
+// Different forks on top of the base chain:
+var testChainForkLightA, testChainForkLightB, testChainForkHeavy *testChain
+
+func init() {
+ var forkLen = int(fullMaxForkAncestry + 50)
+ var wg sync.WaitGroup
+ wg.Add(3)
+ go func() { testChainForkLightA = testChainBase.makeFork(forkLen, false, 1); wg.Done() }()
+ go func() { testChainForkLightB = testChainBase.makeFork(forkLen, false, 2); wg.Done() }()
+ go func() { testChainForkHeavy = testChainBase.makeFork(forkLen, true, 3); wg.Done() }()
+ wg.Wait()
+}
+
+type testChain struct {
+ genesis *types.Block
+ chain []common.Hash
+ headerm map[common.Hash]*types.Header
+ blockm map[common.Hash]*types.Block
+ receiptm map[common.Hash][]*types.Receipt
+ tdm map[common.Hash]*big.Int
+}
+
+// newTestChain creates a blockchain of the given length.
+func newTestChain(length int, genesis *types.Block) *testChain {
+ tc := new(testChain).copy(length)
+ tc.genesis = genesis
+ tc.chain = append(tc.chain, genesis.Hash())
+ tc.headerm[tc.genesis.Hash()] = tc.genesis.Header()
+ tc.tdm[tc.genesis.Hash()] = tc.genesis.Difficulty()
+ tc.blockm[tc.genesis.Hash()] = tc.genesis
+ tc.generate(length-1, 0, genesis, false)
+ return tc
+}
+
+// makeFork creates a fork on top of the test chain.
+func (tc *testChain) makeFork(length int, heavy bool, seed byte) *testChain {
+ fork := tc.copy(tc.len() + length)
+ fork.generate(length, seed, tc.headBlock(), heavy)
+ return fork
+}
+
+// shorten creates a copy of the chain with the given length. It panics if the
+// length is longer than the number of available blocks.
+func (tc *testChain) shorten(length int) *testChain {
+ if length > tc.len() {
+ panic(fmt.Errorf("can't shorten test chain to %d blocks, it's only %d blocks long", length, tc.len()))
+ }
+ return tc.copy(length)
+}
+
+func (tc *testChain) copy(newlen int) *testChain {
+ cpy := &testChain{
+ genesis: tc.genesis,
+ headerm: make(map[common.Hash]*types.Header, newlen),
+ blockm: make(map[common.Hash]*types.Block, newlen),
+ receiptm: make(map[common.Hash][]*types.Receipt, newlen),
+ tdm: make(map[common.Hash]*big.Int, newlen),
+ }
+ for i := 0; i < len(tc.chain) && i < newlen; i++ {
+ hash := tc.chain[i]
+ cpy.chain = append(cpy.chain, tc.chain[i])
+ cpy.tdm[hash] = tc.tdm[hash]
+ cpy.blockm[hash] = tc.blockm[hash]
+ cpy.headerm[hash] = tc.headerm[hash]
+ cpy.receiptm[hash] = tc.receiptm[hash]
+ }
+ return cpy
+}
+
+// generate creates a chain of n blocks starting at and including parent.
+// the returned hash chain is ordered head->parent. In addition, every 22th block
+// contains a transaction and every 5th an uncle to allow testing correct block
+// reassembly.
+func (tc *testChain) generate(n int, seed byte, parent *types.Block, heavy bool) {
+ // start := time.Now()
+ // defer func() { fmt.Printf("test chain generated in %v\n", time.Since(start)) }()
+
+ blocks, receipts := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testDB, n, func(i int, block *core.BlockGen) {
+ block.SetCoinbase(common.Address{seed})
+ // If a heavy chain is requested, delay blocks to raise difficulty
+ if heavy {
+ block.OffsetTime(-1)
+ }
+ // Include transactions to the miner to make blocks more interesting.
+ if parent == tc.genesis && i%22 == 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, block.BaseFee(), nil), signer, testKey)
+ if err != nil {
+ panic(err)
+ }
+ block.AddTx(tx)
+ }
+ // if the block number is a multiple of 5, add a bonus uncle to the block
+ if i > 0 && i%5 == 0 {
+ block.AddUncle(&types.Header{
+ ParentHash: block.PrevBlock(i - 1).Hash(),
+ Number: big.NewInt(block.Number().Int64() - 1),
+ })
+ }
+ })
+
+ // Convert the block-chain into a hash-chain and header/block maps
+ td := new(big.Int).Set(tc.td(parent.Hash()))
+ for i, b := range blocks {
+ td := td.Add(td, b.Difficulty())
+ hash := b.Hash()
+ tc.chain = append(tc.chain, hash)
+ tc.blockm[hash] = b
+ tc.headerm[hash] = b.Header()
+ tc.receiptm[hash] = receipts[i]
+ tc.tdm[hash] = new(big.Int).Set(td)
+ }
+}
+
+// len returns the total number of blocks in the chain.
+func (tc *testChain) len() int {
+ return len(tc.chain)
+}
+
+// headBlock returns the head of the chain.
+func (tc *testChain) headBlock() *types.Block {
+ return tc.blockm[tc.chain[len(tc.chain)-1]]
+}
+
+// td returns the total difficulty of the given block.
+func (tc *testChain) td(hash common.Hash) *big.Int {
+ return tc.tdm[hash]
+}
+
+// headersByHash returns headers in order from the given hash.
+func (tc *testChain) headersByHash(origin common.Hash, amount int, skip int, reverse bool) []*types.Header {
+ num, _ := tc.hashToNumber(origin)
+ return tc.headersByNumber(num, amount, skip, reverse)
+}
+
+// headersByNumber returns headers from the given number.
+func (tc *testChain) headersByNumber(origin uint64, amount int, skip int, reverse bool) []*types.Header {
+ result := make([]*types.Header, 0, amount)
+
+ if !reverse {
+ for num := origin; num < uint64(len(tc.chain)) && len(result) < amount; num += uint64(skip) + 1 {
+ if header, ok := tc.headerm[tc.chain[int(num)]]; ok {
+ result = append(result, header)
+ }
+ }
+ } else {
+ for num := int64(origin); num >= 0 && len(result) < amount; num -= int64(skip) + 1 {
+ if header, ok := tc.headerm[tc.chain[int(num)]]; ok {
+ result = append(result, header)
+ }
+ }
+ }
+ return result
+}
+
+// receipts returns the receipts of the given block hashes.
+func (tc *testChain) receipts(hashes []common.Hash) [][]*types.Receipt {
+ results := make([][]*types.Receipt, 0, len(hashes))
+ for _, hash := range hashes {
+ if receipt, ok := tc.receiptm[hash]; ok {
+ results = append(results, receipt)
+ }
+ }
+ return results
+}
+
+// bodies returns the block bodies of the given block hashes.
+func (tc *testChain) bodies(hashes []common.Hash) ([][]*types.Transaction, [][]*types.Header) {
+ transactions := make([][]*types.Transaction, 0, len(hashes))
+ uncles := make([][]*types.Header, 0, len(hashes))
+ for _, hash := range hashes {
+ if block, ok := tc.blockm[hash]; ok {
+ transactions = append(transactions, block.Transactions())
+ uncles = append(uncles, block.Uncles())
+ }
+ }
+ return transactions, uncles
+}
+
+func (tc *testChain) hashToNumber(target common.Hash) (uint64, bool) {
+ for num, hash := range tc.chain {
+ if hash == target {
+ return uint64(num), true
+ }
+ }
+ return 0, false
+}
diff --git a/les/downloader/types.go b/les/downloader/types.go
new file mode 100644
index 0000000000..ff70bfa0e3
--- /dev/null
+++ b/les/downloader/types.go
@@ -0,0 +1,79 @@
+// 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 downloader
+
+import (
+ "fmt"
+
+ "github.com/ethereum/go-ethereum/core/types"
+)
+
+// peerDropFn is a callback type for dropping a peer detected as malicious.
+type peerDropFn func(id string)
+
+// dataPack is a data message returned by a peer for some query.
+type dataPack interface {
+ PeerId() string
+ Items() int
+ Stats() string
+}
+
+// headerPack is a batch of block headers returned by a peer.
+type headerPack struct {
+ peerID string
+ headers []*types.Header
+}
+
+func (p *headerPack) PeerId() string { return p.peerID }
+func (p *headerPack) Items() int { return len(p.headers) }
+func (p *headerPack) Stats() string { return fmt.Sprintf("%d", len(p.headers)) }
+
+// bodyPack is a batch of block bodies returned by a peer.
+type bodyPack struct {
+ peerID string
+ transactions [][]*types.Transaction
+ uncles [][]*types.Header
+}
+
+func (p *bodyPack) PeerId() string { return p.peerID }
+func (p *bodyPack) Items() int {
+ if len(p.transactions) <= len(p.uncles) {
+ return len(p.transactions)
+ }
+ return len(p.uncles)
+}
+func (p *bodyPack) Stats() string { return fmt.Sprintf("%d:%d", len(p.transactions), len(p.uncles)) }
+
+// receiptPack is a batch of receipts returned by a peer.
+type receiptPack struct {
+ peerID string
+ receipts [][]*types.Receipt
+}
+
+func (p *receiptPack) PeerId() string { return p.peerID }
+func (p *receiptPack) Items() int { return len(p.receipts) }
+func (p *receiptPack) Stats() string { return fmt.Sprintf("%d", len(p.receipts)) }
+
+// statePack is a batch of states returned by a peer.
+type statePack struct {
+ peerID string
+ states [][]byte
+}
+
+func (p *statePack) PeerId() string { return p.peerID }
+func (p *statePack) Items() int { return len(p.states) }
+func (p *statePack) Stats() string { return fmt.Sprintf("%d", len(p.states)) }
diff --git a/les/fetcher.go b/les/fetcher.go
index a6d1c93c4b..d944d32858 100644
--- a/les/fetcher.go
+++ b/les/fetcher.go
@@ -27,8 +27,8 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
- "github.com/ethereum/go-ethereum/eth/fetcher"
"github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/les/fetcher"
"github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/p2p/enode"
@@ -153,9 +153,7 @@ type lightFetcher struct {
synchronise func(peer *serverPeer)
// Test fields or hooks
- noAnnounce bool
newHeadHook func(*types.Header)
- newAnnounce func(*serverPeer, *announceData)
}
// newLightFetcher creates a light fetcher instance.
@@ -474,12 +472,6 @@ func (f *lightFetcher) mainloop() {
// announce processes a new announcement message received from a peer.
func (f *lightFetcher) announce(p *serverPeer, head *announceData) {
- if f.newAnnounce != nil {
- f.newAnnounce(p, head)
- }
- if f.noAnnounce {
- return
- }
select {
case f.announceCh <- &announce{peerid: p.ID(), trust: p.trusted, data: head}:
case <-f.closeCh:
diff --git a/les/fetcher/block_fetcher.go b/les/fetcher/block_fetcher.go
new file mode 100644
index 0000000000..283008db0f
--- /dev/null
+++ b/les/fetcher/block_fetcher.go
@@ -0,0 +1,889 @@
+// 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 .
+
+// This is a temporary package whilst working on the eth/66 blocking refactors.
+// After that work is done, les needs to be refactored to use the new package,
+// or alternatively use a stripped down version of it. Either way, we need to
+// keep the changes scoped so duplicating temporarily seems the sanest.
+package fetcher
+
+import (
+ "errors"
+ "math/rand"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/common/prque"
+ "github.com/ethereum/go-ethereum/consensus"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/metrics"
+ "github.com/ethereum/go-ethereum/trie"
+)
+
+const (
+ lightTimeout = time.Millisecond // Time allowance before an announced header is explicitly requested
+ arriveTimeout = 500 * time.Millisecond // Time allowance before an announced block/transaction is explicitly requested
+ gatherSlack = 100 * time.Millisecond // Interval used to collate almost-expired announces with fetches
+ fetchTimeout = 5 * time.Second // Maximum allotted time to return an explicitly requested block/transaction
+)
+
+const (
+ maxUncleDist = 7 // Maximum allowed backward distance from the chain head
+ maxQueueDist = 32 // Maximum allowed distance from the chain head to queue
+ hashLimit = 256 // Maximum number of unique blocks or headers a peer may have announced
+ blockLimit = 64 // Maximum number of unique blocks a peer may have delivered
+)
+
+var (
+ blockAnnounceInMeter = metrics.NewRegisteredMeter("eth/fetcher/block/announces/in", nil)
+ blockAnnounceOutTimer = metrics.NewRegisteredTimer("eth/fetcher/block/announces/out", nil)
+ blockAnnounceDropMeter = metrics.NewRegisteredMeter("eth/fetcher/block/announces/drop", nil)
+ blockAnnounceDOSMeter = metrics.NewRegisteredMeter("eth/fetcher/block/announces/dos", nil)
+
+ blockBroadcastInMeter = metrics.NewRegisteredMeter("eth/fetcher/block/broadcasts/in", nil)
+ blockBroadcastOutTimer = metrics.NewRegisteredTimer("eth/fetcher/block/broadcasts/out", nil)
+ blockBroadcastDropMeter = metrics.NewRegisteredMeter("eth/fetcher/block/broadcasts/drop", nil)
+ blockBroadcastDOSMeter = metrics.NewRegisteredMeter("eth/fetcher/block/broadcasts/dos", nil)
+
+ headerFetchMeter = metrics.NewRegisteredMeter("eth/fetcher/block/headers", nil)
+ bodyFetchMeter = metrics.NewRegisteredMeter("eth/fetcher/block/bodies", nil)
+
+ headerFilterInMeter = metrics.NewRegisteredMeter("eth/fetcher/block/filter/headers/in", nil)
+ headerFilterOutMeter = metrics.NewRegisteredMeter("eth/fetcher/block/filter/headers/out", nil)
+ bodyFilterInMeter = metrics.NewRegisteredMeter("eth/fetcher/block/filter/bodies/in", nil)
+ bodyFilterOutMeter = metrics.NewRegisteredMeter("eth/fetcher/block/filter/bodies/out", nil)
+)
+
+var errTerminated = errors.New("terminated")
+
+// HeaderRetrievalFn is a callback type for retrieving a header from the local chain.
+type HeaderRetrievalFn func(common.Hash) *types.Header
+
+// blockRetrievalFn is a callback type for retrieving a block from the local chain.
+type blockRetrievalFn func(common.Hash) *types.Block
+
+// headerRequesterFn is a callback type for sending a header retrieval request.
+type headerRequesterFn func(common.Hash) error
+
+// bodyRequesterFn is a callback type for sending a body retrieval request.
+type bodyRequesterFn func([]common.Hash) error
+
+// headerVerifierFn is a callback type to verify a block's header for fast propagation.
+type headerVerifierFn func(header *types.Header) error
+
+// blockBroadcasterFn is a callback type for broadcasting a block to connected peers.
+type blockBroadcasterFn func(block *types.Block, propagate bool)
+
+// chainHeightFn is a callback type to retrieve the current chain height.
+type chainHeightFn func() uint64
+
+// headersInsertFn is a callback type to insert a batch of headers into the local chain.
+type headersInsertFn func(headers []*types.Header) (int, error)
+
+// chainInsertFn is a callback type to insert a batch of blocks into the local chain.
+type chainInsertFn func(types.Blocks) (int, error)
+
+// peerDropFn is a callback type for dropping a peer detected as malicious.
+type peerDropFn func(id string)
+
+// blockAnnounce is the hash notification of the availability of a new block in the
+// network.
+type blockAnnounce struct {
+ hash common.Hash // Hash of the block being announced
+ number uint64 // Number of the block being announced (0 = unknown | old protocol)
+ header *types.Header // Header of the block partially reassembled (new protocol)
+ time time.Time // Timestamp of the announcement
+
+ origin string // Identifier of the peer originating the notification
+
+ fetchHeader headerRequesterFn // Fetcher function to retrieve the header of an announced block
+ fetchBodies bodyRequesterFn // Fetcher function to retrieve the body of an announced block
+}
+
+// headerFilterTask represents a batch of headers needing fetcher filtering.
+type headerFilterTask struct {
+ peer string // The source peer of block headers
+ headers []*types.Header // Collection of headers to filter
+ time time.Time // Arrival time of the headers
+}
+
+// bodyFilterTask represents a batch of block bodies (transactions and uncles)
+// needing fetcher filtering.
+type bodyFilterTask struct {
+ peer string // The source peer of block bodies
+ transactions [][]*types.Transaction // Collection of transactions per block bodies
+ uncles [][]*types.Header // Collection of uncles per block bodies
+ time time.Time // Arrival time of the blocks' contents
+}
+
+// blockOrHeaderInject represents a schedules import operation.
+type blockOrHeaderInject struct {
+ origin string
+
+ header *types.Header // Used for light mode fetcher which only cares about header.
+ block *types.Block // Used for normal mode fetcher which imports full block.
+}
+
+// number returns the block number of the injected object.
+func (inject *blockOrHeaderInject) number() uint64 {
+ if inject.header != nil {
+ return inject.header.Number.Uint64()
+ }
+ return inject.block.NumberU64()
+}
+
+// number returns the block hash of the injected object.
+func (inject *blockOrHeaderInject) hash() common.Hash {
+ if inject.header != nil {
+ return inject.header.Hash()
+ }
+ return inject.block.Hash()
+}
+
+// BlockFetcher is responsible for accumulating block announcements from various peers
+// and scheduling them for retrieval.
+type BlockFetcher struct {
+ light bool // The indicator whether it's a light fetcher or normal one.
+
+ // Various event channels
+ notify chan *blockAnnounce
+ inject chan *blockOrHeaderInject
+
+ headerFilter chan chan *headerFilterTask
+ bodyFilter chan chan *bodyFilterTask
+
+ done chan common.Hash
+ quit chan struct{}
+
+ // Announce states
+ announces map[string]int // Per peer blockAnnounce counts to prevent memory exhaustion
+ announced map[common.Hash][]*blockAnnounce // Announced blocks, scheduled for fetching
+ fetching map[common.Hash]*blockAnnounce // Announced blocks, currently fetching
+ fetched map[common.Hash][]*blockAnnounce // Blocks with headers fetched, scheduled for body retrieval
+ completing map[common.Hash]*blockAnnounce // Blocks with headers, currently body-completing
+
+ // Block cache
+ 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]*blockOrHeaderInject // Set of already queued blocks (to dedup imports)
+
+ // Callbacks
+ getHeader HeaderRetrievalFn // Retrieves a header from the local chain
+ getBlock blockRetrievalFn // Retrieves a block from the local chain
+ verifyHeader headerVerifierFn // Checks if a block's headers have a valid proof of work
+ broadcastBlock blockBroadcasterFn // Broadcasts a block to connected peers
+ chainHeight chainHeightFn // Retrieves the current chain's height
+ insertHeaders headersInsertFn // Injects a batch of headers into the chain
+ insertChain chainInsertFn // Injects a batch of blocks into the chain
+ dropPeer peerDropFn // Drops a peer for misbehaving
+
+ // Testing hooks
+ announceChangeHook func(common.Hash, bool) // Method to call upon adding or deleting a hash from the blockAnnounce list
+ queueChangeHook func(common.Hash, bool) // Method to call upon adding or deleting a block from the import queue
+ fetchingHook func([]common.Hash) // Method to call upon starting a block (eth/61) or header (eth/62) fetch
+ completingHook func([]common.Hash) // Method to call upon starting a block body fetch (eth/62)
+ importedHook func(*types.Header, *types.Block) // Method to call upon successful header or block import (both eth/61 and eth/62)
+}
+
+// NewBlockFetcher creates a block fetcher to retrieve blocks based on hash announcements.
+func NewBlockFetcher(light bool, getHeader HeaderRetrievalFn, getBlock blockRetrievalFn, verifyHeader headerVerifierFn, broadcastBlock blockBroadcasterFn, chainHeight chainHeightFn, insertHeaders headersInsertFn, insertChain chainInsertFn, dropPeer peerDropFn) *BlockFetcher {
+ return &BlockFetcher{
+ light: light,
+ notify: make(chan *blockAnnounce),
+ inject: make(chan *blockOrHeaderInject),
+ headerFilter: make(chan chan *headerFilterTask),
+ bodyFilter: make(chan chan *bodyFilterTask),
+ done: make(chan common.Hash),
+ quit: make(chan struct{}),
+ announces: make(map[string]int),
+ announced: make(map[common.Hash][]*blockAnnounce),
+ fetching: make(map[common.Hash]*blockAnnounce),
+ fetched: make(map[common.Hash][]*blockAnnounce),
+ completing: make(map[common.Hash]*blockAnnounce),
+ queue: prque.New(nil),
+ queues: make(map[string]int),
+ queued: make(map[common.Hash]*blockOrHeaderInject),
+ getHeader: getHeader,
+ getBlock: getBlock,
+ verifyHeader: verifyHeader,
+ broadcastBlock: broadcastBlock,
+ chainHeight: chainHeight,
+ insertHeaders: insertHeaders,
+ insertChain: insertChain,
+ dropPeer: dropPeer,
+ }
+}
+
+// Start boots up the announcement based synchroniser, accepting and processing
+// hash notifications and block fetches until termination requested.
+func (f *BlockFetcher) Start() {
+ go f.loop()
+}
+
+// Stop terminates the announcement based synchroniser, canceling all pending
+// operations.
+func (f *BlockFetcher) Stop() {
+ close(f.quit)
+}
+
+// Notify announces the fetcher of the potential availability of a new block in
+// the network.
+func (f *BlockFetcher) Notify(peer string, hash common.Hash, number uint64, time time.Time,
+ headerFetcher headerRequesterFn, bodyFetcher bodyRequesterFn) error {
+ block := &blockAnnounce{
+ hash: hash,
+ number: number,
+ time: time,
+ origin: peer,
+ fetchHeader: headerFetcher,
+ fetchBodies: bodyFetcher,
+ }
+ select {
+ case f.notify <- block:
+ return nil
+ case <-f.quit:
+ return errTerminated
+ }
+}
+
+// Enqueue tries to fill gaps the fetcher's future import queue.
+func (f *BlockFetcher) Enqueue(peer string, block *types.Block) error {
+ op := &blockOrHeaderInject{
+ origin: peer,
+ block: block,
+ }
+ select {
+ case f.inject <- op:
+ return nil
+ case <-f.quit:
+ return errTerminated
+ }
+}
+
+// FilterHeaders extracts all the headers that were explicitly requested by the fetcher,
+// returning those that should be handled differently.
+func (f *BlockFetcher) FilterHeaders(peer string, headers []*types.Header, time time.Time) []*types.Header {
+ log.Trace("Filtering headers", "peer", peer, "headers", len(headers))
+
+ // Send the filter channel to the fetcher
+ filter := make(chan *headerFilterTask)
+
+ select {
+ case f.headerFilter <- filter:
+ case <-f.quit:
+ return nil
+ }
+ // Request the filtering of the header list
+ select {
+ case filter <- &headerFilterTask{peer: peer, headers: headers, time: time}:
+ case <-f.quit:
+ return nil
+ }
+ // Retrieve the headers remaining after filtering
+ select {
+ case task := <-filter:
+ return task.headers
+ case <-f.quit:
+ return nil
+ }
+}
+
+// FilterBodies extracts all the block bodies that were explicitly requested by
+// the fetcher, returning those that should be handled differently.
+func (f *BlockFetcher) FilterBodies(peer string, transactions [][]*types.Transaction, uncles [][]*types.Header, time time.Time) ([][]*types.Transaction, [][]*types.Header) {
+ log.Trace("Filtering bodies", "peer", peer, "txs", len(transactions), "uncles", len(uncles))
+
+ // Send the filter channel to the fetcher
+ filter := make(chan *bodyFilterTask)
+
+ select {
+ case f.bodyFilter <- filter:
+ case <-f.quit:
+ return nil, nil
+ }
+ // Request the filtering of the body list
+ select {
+ case filter <- &bodyFilterTask{peer: peer, transactions: transactions, uncles: uncles, time: time}:
+ case <-f.quit:
+ return nil, nil
+ }
+ // Retrieve the bodies remaining after filtering
+ select {
+ case task := <-filter:
+ return task.transactions, task.uncles
+ case <-f.quit:
+ return nil, nil
+ }
+}
+
+// Loop is the main fetcher loop, checking and processing various notification
+// events.
+func (f *BlockFetcher) loop() {
+ // Iterate the block fetching until a quit is requested
+ var (
+ fetchTimer = time.NewTimer(0)
+ completeTimer = time.NewTimer(0)
+ )
+ <-fetchTimer.C // clear out the channel
+ <-completeTimer.C
+ defer fetchTimer.Stop()
+ defer completeTimer.Stop()
+
+ for {
+ // Clean up any expired block fetches
+ for hash, announce := range f.fetching {
+ if time.Since(announce.time) > fetchTimeout {
+ f.forgetHash(hash)
+ }
+ }
+ // Import any queued blocks that could potentially fit
+ height := f.chainHeight()
+ for !f.queue.Empty() {
+ op := f.queue.PopItem().(*blockOrHeaderInject)
+ hash := op.hash()
+ if f.queueChangeHook != nil {
+ f.queueChangeHook(hash, false)
+ }
+ // If too high up the chain or phase, continue later
+ number := op.number()
+ if number > height+1 {
+ f.queue.Push(op, -int64(number))
+ if f.queueChangeHook != nil {
+ f.queueChangeHook(hash, true)
+ }
+ break
+ }
+ // Otherwise if fresh and still unknown, try and import
+ if (number+maxUncleDist < height) || (f.light && f.getHeader(hash) != nil) || (!f.light && f.getBlock(hash) != nil) {
+ f.forgetBlock(hash)
+ continue
+ }
+ if f.light {
+ f.importHeaders(op.origin, op.header)
+ } else {
+ f.importBlocks(op.origin, op.block)
+ }
+ }
+ // Wait for an outside event to occur
+ select {
+ case <-f.quit:
+ // BlockFetcher terminating, abort all operations
+ return
+
+ case notification := <-f.notify:
+ // A block was announced, make sure the peer isn't DOSing us
+ blockAnnounceInMeter.Mark(1)
+
+ count := f.announces[notification.origin] + 1
+ if count > hashLimit {
+ log.Debug("Peer exceeded outstanding announces", "peer", notification.origin, "limit", hashLimit)
+ blockAnnounceDOSMeter.Mark(1)
+ break
+ }
+ // If we have a valid block number, check that it's potentially useful
+ if notification.number > 0 {
+ if dist := int64(notification.number) - int64(f.chainHeight()); dist < -maxUncleDist || dist > maxQueueDist {
+ log.Debug("Peer discarded announcement", "peer", notification.origin, "number", notification.number, "hash", notification.hash, "distance", dist)
+ blockAnnounceDropMeter.Mark(1)
+ break
+ }
+ }
+ // All is well, schedule the announce if block's not yet downloading
+ if _, ok := f.fetching[notification.hash]; ok {
+ break
+ }
+ if _, ok := f.completing[notification.hash]; ok {
+ break
+ }
+ f.announces[notification.origin] = count
+ f.announced[notification.hash] = append(f.announced[notification.hash], notification)
+ if f.announceChangeHook != nil && len(f.announced[notification.hash]) == 1 {
+ f.announceChangeHook(notification.hash, true)
+ }
+ if len(f.announced) == 1 {
+ f.rescheduleFetch(fetchTimer)
+ }
+
+ case op := <-f.inject:
+ // A direct block insertion was requested, try and fill any pending gaps
+ blockBroadcastInMeter.Mark(1)
+
+ // Now only direct block injection is allowed, drop the header injection
+ // here silently if we receive.
+ if f.light {
+ continue
+ }
+ f.enqueue(op.origin, nil, op.block)
+
+ case hash := <-f.done:
+ // A pending import finished, remove all traces of the notification
+ f.forgetHash(hash)
+ f.forgetBlock(hash)
+
+ case <-fetchTimer.C:
+ // At least one block's timer ran out, check for needing retrieval
+ request := make(map[string][]common.Hash)
+
+ for hash, announces := range f.announced {
+ // In current LES protocol(les2/les3), only header announce is
+ // available, no need to wait too much time for header broadcast.
+ timeout := arriveTimeout - gatherSlack
+ if f.light {
+ timeout = 0
+ }
+ if time.Since(announces[0].time) > timeout {
+ // Pick a random peer to retrieve from, reset all others
+ announce := announces[rand.Intn(len(announces))]
+ f.forgetHash(hash)
+
+ // If the block still didn't arrive, queue for fetching
+ if (f.light && f.getHeader(hash) == nil) || (!f.light && f.getBlock(hash) == nil) {
+ request[announce.origin] = append(request[announce.origin], hash)
+ f.fetching[hash] = announce
+ }
+ }
+ }
+ // Send out all block header requests
+ for peer, hashes := range request {
+ log.Trace("Fetching scheduled headers", "peer", peer, "list", hashes)
+
+ // Create a closure of the fetch and schedule in on a new thread
+ fetchHeader, hashes := f.fetching[hashes[0]].fetchHeader, hashes
+ go func() {
+ if f.fetchingHook != nil {
+ f.fetchingHook(hashes)
+ }
+ for _, hash := range hashes {
+ headerFetchMeter.Mark(1)
+ fetchHeader(hash) // Suboptimal, but protocol doesn't allow batch header retrievals
+ }
+ }()
+ }
+ // Schedule the next fetch if blocks are still pending
+ f.rescheduleFetch(fetchTimer)
+
+ case <-completeTimer.C:
+ // At least one header's timer ran out, retrieve everything
+ request := make(map[string][]common.Hash)
+
+ for hash, announces := range f.fetched {
+ // Pick a random peer to retrieve from, reset all others
+ announce := announces[rand.Intn(len(announces))]
+ f.forgetHash(hash)
+
+ // If the block still didn't arrive, queue for completion
+ if f.getBlock(hash) == nil {
+ request[announce.origin] = append(request[announce.origin], hash)
+ f.completing[hash] = announce
+ }
+ }
+ // Send out all block body requests
+ for peer, hashes := range request {
+ log.Trace("Fetching scheduled bodies", "peer", peer, "list", hashes)
+
+ // Create a closure of the fetch and schedule in on a new thread
+ if f.completingHook != nil {
+ f.completingHook(hashes)
+ }
+ bodyFetchMeter.Mark(int64(len(hashes)))
+ go f.completing[hashes[0]].fetchBodies(hashes)
+ }
+ // Schedule the next fetch if blocks are still pending
+ f.rescheduleComplete(completeTimer)
+
+ case filter := <-f.headerFilter:
+ // Headers arrived from a remote peer. Extract those that were explicitly
+ // requested by the fetcher, and return everything else so it's delivered
+ // to other parts of the system.
+ var task *headerFilterTask
+ select {
+ case task = <-filter:
+ case <-f.quit:
+ return
+ }
+ headerFilterInMeter.Mark(int64(len(task.headers)))
+
+ // Split the batch of headers into unknown ones (to return to the caller),
+ // known incomplete ones (requiring body retrievals) and completed blocks.
+ unknown, incomplete, complete, lightHeaders := []*types.Header{}, []*blockAnnounce{}, []*types.Block{}, []*blockAnnounce{}
+ for _, header := range task.headers {
+ hash := header.Hash()
+
+ // Filter fetcher-requested headers from other synchronisation algorithms
+ if announce := f.fetching[hash]; announce != nil && announce.origin == task.peer && f.fetched[hash] == nil && f.completing[hash] == nil && f.queued[hash] == nil {
+ // If the delivered header does not match the promised number, drop the announcer
+ if header.Number.Uint64() != announce.number {
+ log.Trace("Invalid block number fetched", "peer", announce.origin, "hash", header.Hash(), "announced", announce.number, "provided", header.Number)
+ f.dropPeer(announce.origin)
+ f.forgetHash(hash)
+ continue
+ }
+ // Collect all headers only if we are running in light
+ // mode and the headers are not imported by other means.
+ if f.light {
+ if f.getHeader(hash) == nil {
+ announce.header = header
+ lightHeaders = append(lightHeaders, announce)
+ }
+ f.forgetHash(hash)
+ continue
+ }
+ // Only keep if not imported by other means
+ if f.getBlock(hash) == nil {
+ announce.header = header
+ announce.time = task.time
+
+ // If the block is empty (header only), short circuit into the final import queue
+ if header.TxHash == types.EmptyRootHash && header.UncleHash == types.EmptyUncleHash {
+ log.Trace("Block empty, skipping body retrieval", "peer", announce.origin, "number", header.Number, "hash", header.Hash())
+
+ block := types.NewBlockWithHeader(header)
+ block.ReceivedAt = task.time
+
+ complete = append(complete, block)
+ f.completing[hash] = announce
+ continue
+ }
+ // Otherwise add to the list of blocks needing completion
+ incomplete = append(incomplete, announce)
+ } else {
+ log.Trace("Block already imported, discarding header", "peer", announce.origin, "number", header.Number, "hash", header.Hash())
+ f.forgetHash(hash)
+ }
+ } else {
+ // BlockFetcher doesn't know about it, add to the return list
+ unknown = append(unknown, header)
+ }
+ }
+ headerFilterOutMeter.Mark(int64(len(unknown)))
+ select {
+ case filter <- &headerFilterTask{headers: unknown, time: task.time}:
+ case <-f.quit:
+ return
+ }
+ // Schedule the retrieved headers for body completion
+ for _, announce := range incomplete {
+ hash := announce.header.Hash()
+ if _, ok := f.completing[hash]; ok {
+ continue
+ }
+ f.fetched[hash] = append(f.fetched[hash], announce)
+ if len(f.fetched) == 1 {
+ f.rescheduleComplete(completeTimer)
+ }
+ }
+ // Schedule the header for light fetcher import
+ for _, announce := range lightHeaders {
+ f.enqueue(announce.origin, announce.header, nil)
+ }
+ // Schedule the header-only blocks for import
+ for _, block := range complete {
+ if announce := f.completing[block.Hash()]; announce != nil {
+ f.enqueue(announce.origin, nil, block)
+ }
+ }
+
+ case filter := <-f.bodyFilter:
+ // Block bodies arrived, extract any explicitly requested blocks, return the rest
+ var task *bodyFilterTask
+ select {
+ case task = <-filter:
+ case <-f.quit:
+ return
+ }
+ bodyFilterInMeter.Mark(int64(len(task.transactions)))
+ blocks := []*types.Block{}
+ // abort early if there's nothing explicitly requested
+ if len(f.completing) > 0 {
+ for i := 0; i < len(task.transactions) && i < len(task.uncles); i++ {
+ // Match up a body to any possible completion request
+ var (
+ matched = false
+ uncleHash common.Hash // calculated lazily and reused
+ txnHash common.Hash // calculated lazily and reused
+ )
+ for hash, announce := range f.completing {
+ if f.queued[hash] != nil || announce.origin != task.peer {
+ continue
+ }
+ if uncleHash == (common.Hash{}) {
+ uncleHash = types.CalcUncleHash(task.uncles[i])
+ }
+ if uncleHash != announce.header.UncleHash {
+ continue
+ }
+ if txnHash == (common.Hash{}) {
+ txnHash = types.DeriveSha(types.Transactions(task.transactions[i]), trie.NewStackTrie(nil))
+ }
+ if txnHash != announce.header.TxHash {
+ continue
+ }
+ // Mark the body matched, reassemble if still unknown
+ matched = true
+ if f.getBlock(hash) == nil {
+ block := types.NewBlockWithHeader(announce.header).WithBody(task.transactions[i], task.uncles[i])
+ block.ReceivedAt = task.time
+ blocks = append(blocks, block)
+ } else {
+ f.forgetHash(hash)
+ }
+
+ }
+ if matched {
+ task.transactions = append(task.transactions[:i], task.transactions[i+1:]...)
+ task.uncles = append(task.uncles[:i], task.uncles[i+1:]...)
+ i--
+ continue
+ }
+ }
+ }
+ bodyFilterOutMeter.Mark(int64(len(task.transactions)))
+ select {
+ case filter <- task:
+ case <-f.quit:
+ return
+ }
+ // Schedule the retrieved blocks for ordered import
+ for _, block := range blocks {
+ if announce := f.completing[block.Hash()]; announce != nil {
+ f.enqueue(announce.origin, nil, block)
+ }
+ }
+ }
+ }
+}
+
+// rescheduleFetch resets the specified fetch timer to the next blockAnnounce timeout.
+func (f *BlockFetcher) rescheduleFetch(fetch *time.Timer) {
+ // Short circuit if no blocks are announced
+ if len(f.announced) == 0 {
+ return
+ }
+ // Schedule announcement retrieval quickly for light mode
+ // since server won't send any headers to client.
+ if f.light {
+ fetch.Reset(lightTimeout)
+ return
+ }
+ // Otherwise find the earliest expiring announcement
+ earliest := time.Now()
+ for _, announces := range f.announced {
+ if earliest.After(announces[0].time) {
+ earliest = announces[0].time
+ }
+ }
+ fetch.Reset(arriveTimeout - time.Since(earliest))
+}
+
+// rescheduleComplete resets the specified completion timer to the next fetch timeout.
+func (f *BlockFetcher) rescheduleComplete(complete *time.Timer) {
+ // Short circuit if no headers are fetched
+ if len(f.fetched) == 0 {
+ return
+ }
+ // Otherwise find the earliest expiring announcement
+ earliest := time.Now()
+ for _, announces := range f.fetched {
+ if earliest.After(announces[0].time) {
+ earliest = announces[0].time
+ }
+ }
+ complete.Reset(gatherSlack - time.Since(earliest))
+}
+
+// enqueue schedules a new header or block import operation, if the component
+// to be imported has not yet been seen.
+func (f *BlockFetcher) enqueue(peer string, header *types.Header, block *types.Block) {
+ var (
+ hash common.Hash
+ number uint64
+ )
+ if header != nil {
+ hash, number = header.Hash(), header.Number.Uint64()
+ } else {
+ hash, number = block.Hash(), block.NumberU64()
+ }
+ // Ensure the peer isn't DOSing us
+ count := f.queues[peer] + 1
+ if count > blockLimit {
+ log.Debug("Discarded delivered header or block, exceeded allowance", "peer", peer, "number", number, "hash", hash, "limit", blockLimit)
+ blockBroadcastDOSMeter.Mark(1)
+ f.forgetHash(hash)
+ return
+ }
+ // Discard any past or too distant blocks
+ if dist := int64(number) - int64(f.chainHeight()); dist < -maxUncleDist || dist > maxQueueDist {
+ log.Debug("Discarded delivered header or block, too far away", "peer", peer, "number", number, "hash", hash, "distance", dist)
+ blockBroadcastDropMeter.Mark(1)
+ f.forgetHash(hash)
+ return
+ }
+ // Schedule the block for future importing
+ if _, ok := f.queued[hash]; !ok {
+ op := &blockOrHeaderInject{origin: peer}
+ if header != nil {
+ op.header = header
+ } else {
+ op.block = block
+ }
+ f.queues[peer] = count
+ f.queued[hash] = op
+ f.queue.Push(op, -int64(number))
+ if f.queueChangeHook != nil {
+ f.queueChangeHook(hash, true)
+ }
+ log.Debug("Queued delivered header or block", "peer", peer, "number", number, "hash", hash, "queued", f.queue.Size())
+ }
+}
+
+// importHeaders spawns a new goroutine to run a header insertion into the chain.
+// If the header's number is at the same height as the current import phase, it
+// updates the phase states accordingly.
+func (f *BlockFetcher) importHeaders(peer string, header *types.Header) {
+ hash := header.Hash()
+ log.Debug("Importing propagated header", "peer", peer, "number", header.Number, "hash", hash)
+
+ go func() {
+ defer func() { f.done <- hash }()
+ // If the parent's unknown, abort insertion
+ parent := f.getHeader(header.ParentHash)
+ if parent == nil {
+ log.Debug("Unknown parent of propagated header", "peer", peer, "number", header.Number, "hash", hash, "parent", header.ParentHash)
+ return
+ }
+ // Validate the header and if something went wrong, drop the peer
+ if err := f.verifyHeader(header); err != nil && err != consensus.ErrFutureBlock {
+ log.Debug("Propagated header verification failed", "peer", peer, "number", header.Number, "hash", hash, "err", err)
+ f.dropPeer(peer)
+ return
+ }
+ // Run the actual import and log any issues
+ if _, err := f.insertHeaders([]*types.Header{header}); err != nil {
+ log.Debug("Propagated header import failed", "peer", peer, "number", header.Number, "hash", hash, "err", err)
+ return
+ }
+ // Invoke the testing hook if needed
+ if f.importedHook != nil {
+ f.importedHook(header, nil)
+ }
+ }()
+}
+
+// importBlocks spawns a new goroutine to run a block insertion into the chain. If the
+// block's number is at the same height as the current import phase, it updates
+// the phase states accordingly.
+func (f *BlockFetcher) importBlocks(peer string, block *types.Block) {
+ hash := block.Hash()
+
+ // Run the import on a new thread
+ log.Debug("Importing propagated block", "peer", peer, "number", block.Number(), "hash", hash)
+ go func() {
+ defer func() { f.done <- hash }()
+
+ // If the parent's unknown, abort insertion
+ parent := f.getBlock(block.ParentHash())
+ if parent == nil {
+ log.Debug("Unknown parent of propagated block", "peer", peer, "number", block.Number(), "hash", hash, "parent", block.ParentHash())
+ return
+ }
+ // Quickly validate the header and propagate the block if it passes
+ switch err := f.verifyHeader(block.Header()); err {
+ case nil:
+ // All ok, quickly propagate to our peers
+ blockBroadcastOutTimer.UpdateSince(block.ReceivedAt)
+ go f.broadcastBlock(block, true)
+
+ case consensus.ErrFutureBlock:
+ // Weird future block, don't fail, but neither propagate
+
+ default:
+ // Something went very wrong, drop the peer
+ log.Debug("Propagated block verification failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err)
+ f.dropPeer(peer)
+ return
+ }
+ // Run the actual import and log any issues
+ if _, err := f.insertChain(types.Blocks{block}); err != nil {
+ log.Debug("Propagated block import failed", "peer", peer, "number", block.Number(), "hash", hash, "err", err)
+ return
+ }
+ // If import succeeded, broadcast the block
+ blockAnnounceOutTimer.UpdateSince(block.ReceivedAt)
+ go f.broadcastBlock(block, false)
+
+ // Invoke the testing hook if needed
+ if f.importedHook != nil {
+ f.importedHook(nil, block)
+ }
+ }()
+}
+
+// forgetHash removes all traces of a block announcement from the fetcher's
+// internal state.
+func (f *BlockFetcher) forgetHash(hash common.Hash) {
+ // Remove all pending announces and decrement DOS counters
+ if announceMap, ok := f.announced[hash]; ok {
+ for _, announce := range announceMap {
+ f.announces[announce.origin]--
+ if f.announces[announce.origin] <= 0 {
+ delete(f.announces, announce.origin)
+ }
+ }
+ delete(f.announced, hash)
+ if f.announceChangeHook != nil {
+ f.announceChangeHook(hash, false)
+ }
+ }
+ // Remove any pending fetches and decrement the DOS counters
+ if announce := f.fetching[hash]; announce != nil {
+ f.announces[announce.origin]--
+ if f.announces[announce.origin] <= 0 {
+ delete(f.announces, announce.origin)
+ }
+ delete(f.fetching, hash)
+ }
+
+ // Remove any pending completion requests and decrement the DOS counters
+ for _, announce := range f.fetched[hash] {
+ f.announces[announce.origin]--
+ if f.announces[announce.origin] <= 0 {
+ delete(f.announces, announce.origin)
+ }
+ }
+ delete(f.fetched, hash)
+
+ // Remove any pending completions and decrement the DOS counters
+ if announce := f.completing[hash]; announce != nil {
+ f.announces[announce.origin]--
+ if f.announces[announce.origin] <= 0 {
+ delete(f.announces, announce.origin)
+ }
+ delete(f.completing, hash)
+ }
+}
+
+// forgetBlock removes all traces of a queued block from the fetcher's internal
+// state.
+func (f *BlockFetcher) forgetBlock(hash common.Hash) {
+ if insert := f.queued[hash]; insert != nil {
+ f.queues[insert.origin]--
+ if f.queues[insert.origin] == 0 {
+ delete(f.queues, insert.origin)
+ }
+ delete(f.queued, hash)
+ }
+}
diff --git a/les/fetcher/block_fetcher_test.go b/les/fetcher/block_fetcher_test.go
new file mode 100644
index 0000000000..b6d1125b56
--- /dev/null
+++ b/les/fetcher/block_fetcher_test.go
@@ -0,0 +1,896 @@
+// 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 fetcher
+
+import (
+ "errors"
+ "math/big"
+ "sync"
+ "sync/atomic"
+ "testing"
+ "time"
+
+ "github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/consensus/ethash"
+ "github.com/ethereum/go-ethereum/core"
+ "github.com/ethereum/go-ethereum/core/rawdb"
+ "github.com/ethereum/go-ethereum/core/types"
+ "github.com/ethereum/go-ethereum/crypto"
+ "github.com/ethereum/go-ethereum/params"
+ "github.com/ethereum/go-ethereum/trie"
+)
+
+var (
+ testdb = rawdb.NewMemoryDatabase()
+ testKey, _ = crypto.HexToECDSA("b71c71a67e1177ad4e901695e1b4b9ee17ae16c6668d313eac2f96dbcda3f291")
+ testAddress = crypto.PubkeyToAddress(testKey.PublicKey)
+ genesis = core.GenesisBlockForTesting(testdb, testAddress, big.NewInt(1000000000000000))
+ unknownBlock = types.NewBlock(&types.Header{GasLimit: params.GenesisGasLimit, BaseFee: big.NewInt(params.InitialBaseFee)}, nil, nil, nil, trie.NewStackTrie(nil))
+)
+
+// makeChain creates a chain of n blocks starting at and including parent.
+// the returned hash chain is ordered head->parent. In addition, every 3rd block
+// contains a transaction and every 5th an uncle to allow testing correct block
+// reassembly.
+func makeChain(n int, seed byte, parent *types.Block) ([]common.Hash, map[common.Hash]*types.Block) {
+ blocks, _ := core.GenerateChain(params.TestChainConfig, parent, ethash.NewFaker(), testdb, n, func(i int, block *core.BlockGen) {
+ block.SetCoinbase(common.Address{seed})
+
+ // If the block number is multiple of 3, send a bonus transaction to the miner
+ if parent == 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, block.BaseFee(), nil), signer, testKey)
+ if err != nil {
+ panic(err)
+ }
+ block.AddTx(tx)
+ }
+ // If the block number is a multiple of 5, add a bonus uncle to the block
+ if i%5 == 0 {
+ block.AddUncle(&types.Header{ParentHash: block.PrevBlock(i - 1).Hash(), Number: big.NewInt(int64(i - 1))})
+ }
+ })
+ hashes := make([]common.Hash, n+1)
+ hashes[len(hashes)-1] = parent.Hash()
+ blockm := make(map[common.Hash]*types.Block, n+1)
+ blockm[parent.Hash()] = parent
+ for i, b := range blocks {
+ hashes[len(hashes)-i-2] = b.Hash()
+ blockm[b.Hash()] = b
+ }
+ return hashes, blockm
+}
+
+// fetcherTester is a test simulator for mocking out local block chain.
+type fetcherTester struct {
+ fetcher *BlockFetcher
+
+ hashes []common.Hash // Hash chain belonging to the tester
+ headers map[common.Hash]*types.Header // Headers belonging to the tester
+ blocks map[common.Hash]*types.Block // Blocks belonging to the tester
+ drops map[string]bool // Map of peers dropped by the fetcher
+
+ lock sync.RWMutex
+}
+
+// newTester creates a new fetcher test mocker.
+func newTester(light bool) *fetcherTester {
+ tester := &fetcherTester{
+ hashes: []common.Hash{genesis.Hash()},
+ headers: map[common.Hash]*types.Header{genesis.Hash(): genesis.Header()},
+ blocks: map[common.Hash]*types.Block{genesis.Hash(): genesis},
+ drops: make(map[string]bool),
+ }
+ tester.fetcher = NewBlockFetcher(light, tester.getHeader, tester.getBlock, tester.verifyHeader, tester.broadcastBlock, tester.chainHeight, tester.insertHeaders, tester.insertChain, tester.dropPeer)
+ tester.fetcher.Start()
+
+ return tester
+}
+
+// getHeader retrieves a header from the tester's block chain.
+func (f *fetcherTester) getHeader(hash common.Hash) *types.Header {
+ f.lock.RLock()
+ defer f.lock.RUnlock()
+
+ return f.headers[hash]
+}
+
+// getBlock retrieves a block from the tester's block chain.
+func (f *fetcherTester) getBlock(hash common.Hash) *types.Block {
+ f.lock.RLock()
+ defer f.lock.RUnlock()
+
+ return f.blocks[hash]
+}
+
+// verifyHeader is a nop placeholder for the block header verification.
+func (f *fetcherTester) verifyHeader(header *types.Header) error {
+ return nil
+}
+
+// broadcastBlock is a nop placeholder for the block broadcasting.
+func (f *fetcherTester) broadcastBlock(block *types.Block, propagate bool) {
+}
+
+// chainHeight retrieves the current height (block number) of the chain.
+func (f *fetcherTester) chainHeight() uint64 {
+ f.lock.RLock()
+ defer f.lock.RUnlock()
+
+ if f.fetcher.light {
+ return f.headers[f.hashes[len(f.hashes)-1]].Number.Uint64()
+ }
+ return f.blocks[f.hashes[len(f.hashes)-1]].NumberU64()
+}
+
+// insertChain injects a new headers into the simulated chain.
+func (f *fetcherTester) insertHeaders(headers []*types.Header) (int, error) {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+
+ for i, header := range headers {
+ // Make sure the parent in known
+ if _, ok := f.headers[header.ParentHash]; !ok {
+ return i, errors.New("unknown parent")
+ }
+ // Discard any new blocks if the same height already exists
+ if header.Number.Uint64() <= f.headers[f.hashes[len(f.hashes)-1]].Number.Uint64() {
+ return i, nil
+ }
+ // Otherwise build our current chain
+ f.hashes = append(f.hashes, header.Hash())
+ f.headers[header.Hash()] = header
+ }
+ return 0, nil
+}
+
+// insertChain injects a new blocks into the simulated chain.
+func (f *fetcherTester) insertChain(blocks types.Blocks) (int, error) {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+
+ for i, block := range blocks {
+ // Make sure the parent in known
+ if _, ok := f.blocks[block.ParentHash()]; !ok {
+ return i, errors.New("unknown parent")
+ }
+ // Discard any new blocks if the same height already exists
+ if block.NumberU64() <= f.blocks[f.hashes[len(f.hashes)-1]].NumberU64() {
+ return i, nil
+ }
+ // Otherwise build our current chain
+ f.hashes = append(f.hashes, block.Hash())
+ f.blocks[block.Hash()] = block
+ }
+ return 0, nil
+}
+
+// dropPeer is an emulator for the peer removal, simply accumulating the various
+// peers dropped by the fetcher.
+func (f *fetcherTester) dropPeer(peer string) {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+
+ f.drops[peer] = true
+}
+
+// makeHeaderFetcher retrieves a block header fetcher associated with a simulated peer.
+func (f *fetcherTester) makeHeaderFetcher(peer string, blocks map[common.Hash]*types.Block, drift time.Duration) headerRequesterFn {
+ closure := make(map[common.Hash]*types.Block)
+ for hash, block := range blocks {
+ closure[hash] = block
+ }
+ // Create a function that return a header from the closure
+ return func(hash common.Hash) error {
+ // Gather the blocks to return
+ headers := make([]*types.Header, 0, 1)
+ if block, ok := closure[hash]; ok {
+ headers = append(headers, block.Header())
+ }
+ // Return on a new thread
+ go f.fetcher.FilterHeaders(peer, headers, time.Now().Add(drift))
+
+ return nil
+ }
+}
+
+// makeBodyFetcher retrieves a block body fetcher associated with a simulated peer.
+func (f *fetcherTester) makeBodyFetcher(peer string, blocks map[common.Hash]*types.Block, drift time.Duration) bodyRequesterFn {
+ closure := make(map[common.Hash]*types.Block)
+ for hash, block := range blocks {
+ closure[hash] = block
+ }
+ // Create a function that returns blocks from the closure
+ return func(hashes []common.Hash) error {
+ // Gather the block bodies to return
+ transactions := make([][]*types.Transaction, 0, len(hashes))
+ uncles := make([][]*types.Header, 0, len(hashes))
+
+ for _, hash := range hashes {
+ if block, ok := closure[hash]; ok {
+ transactions = append(transactions, block.Transactions())
+ uncles = append(uncles, block.Uncles())
+ }
+ }
+ // Return on a new thread
+ go f.fetcher.FilterBodies(peer, transactions, uncles, time.Now().Add(drift))
+
+ return nil
+ }
+}
+
+// verifyFetchingEvent verifies that one single event arrive on a fetching channel.
+func verifyFetchingEvent(t *testing.T, fetching chan []common.Hash, arrive bool) {
+ if arrive {
+ select {
+ case <-fetching:
+ case <-time.After(time.Second):
+ t.Fatalf("fetching timeout")
+ }
+ } else {
+ select {
+ case <-fetching:
+ t.Fatalf("fetching invoked")
+ case <-time.After(10 * time.Millisecond):
+ }
+ }
+}
+
+// verifyCompletingEvent verifies that one single event arrive on an completing channel.
+func verifyCompletingEvent(t *testing.T, completing chan []common.Hash, arrive bool) {
+ if arrive {
+ select {
+ case <-completing:
+ case <-time.After(time.Second):
+ t.Fatalf("completing timeout")
+ }
+ } else {
+ select {
+ case <-completing:
+ t.Fatalf("completing invoked")
+ case <-time.After(10 * time.Millisecond):
+ }
+ }
+}
+
+// verifyImportEvent verifies that one single event arrive on an import channel.
+func verifyImportEvent(t *testing.T, imported chan interface{}, arrive bool) {
+ if arrive {
+ select {
+ case <-imported:
+ case <-time.After(time.Second):
+ t.Fatalf("import timeout")
+ }
+ } else {
+ select {
+ case <-imported:
+ t.Fatalf("import invoked")
+ case <-time.After(20 * time.Millisecond):
+ }
+ }
+}
+
+// verifyImportCount verifies that exactly count number of events arrive on an
+// import hook channel.
+func verifyImportCount(t *testing.T, imported chan interface{}, count int) {
+ for i := 0; i < count; i++ {
+ select {
+ case <-imported:
+ case <-time.After(time.Second):
+ t.Fatalf("block %d: import timeout", i+1)
+ }
+ }
+ verifyImportDone(t, imported)
+}
+
+// verifyImportDone verifies that no more events are arriving on an import channel.
+func verifyImportDone(t *testing.T, imported chan interface{}) {
+ select {
+ case <-imported:
+ t.Fatalf("extra block imported")
+ case <-time.After(50 * time.Millisecond):
+ }
+}
+
+// verifyChainHeight verifies the chain height is as expected.
+func verifyChainHeight(t *testing.T, fetcher *fetcherTester, height uint64) {
+ if fetcher.chainHeight() != height {
+ t.Fatalf("chain height mismatch, got %d, want %d", fetcher.chainHeight(), height)
+ }
+}
+
+// Tests that a fetcher accepts block/header announcements and initiates retrievals
+// for them, successfully importing into the local chain.
+func TestFullSequentialAnnouncements(t *testing.T) { testSequentialAnnouncements(t, false) }
+func TestLightSequentialAnnouncements(t *testing.T) { testSequentialAnnouncements(t, true) }
+
+func testSequentialAnnouncements(t *testing.T, light bool) {
+ // Create a chain of blocks to import
+ targetBlocks := 4 * hashLimit
+ hashes, blocks := makeChain(targetBlocks, 0, genesis)
+
+ tester := newTester(light)
+ headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack)
+ bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0)
+
+ // Iteratively announce blocks until all are imported
+ imported := make(chan interface{})
+ tester.fetcher.importedHook = func(header *types.Header, block *types.Block) {
+ if light {
+ if header == nil {
+ t.Fatalf("Fetcher try to import empty header")
+ }
+ imported <- header
+ } else {
+ if block == nil {
+ t.Fatalf("Fetcher try to import empty block")
+ }
+ imported <- block
+ }
+ }
+ for i := len(hashes) - 2; i >= 0; i-- {
+ tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
+ verifyImportEvent(t, imported, true)
+ }
+ verifyImportDone(t, imported)
+ verifyChainHeight(t, tester, uint64(len(hashes)-1))
+}
+
+// Tests that if blocks are announced by multiple peers (or even the same buggy
+// peer), they will only get downloaded at most once.
+func TestFullConcurrentAnnouncements(t *testing.T) { testConcurrentAnnouncements(t, false) }
+func TestLightConcurrentAnnouncements(t *testing.T) { testConcurrentAnnouncements(t, true) }
+
+func testConcurrentAnnouncements(t *testing.T, light bool) {
+ // Create a chain of blocks to import
+ targetBlocks := 4 * hashLimit
+ hashes, blocks := makeChain(targetBlocks, 0, genesis)
+
+ // Assemble a tester with a built in counter for the requests
+ tester := newTester(light)
+ firstHeaderFetcher := tester.makeHeaderFetcher("first", blocks, -gatherSlack)
+ firstBodyFetcher := tester.makeBodyFetcher("first", blocks, 0)
+ secondHeaderFetcher := tester.makeHeaderFetcher("second", blocks, -gatherSlack)
+ secondBodyFetcher := tester.makeBodyFetcher("second", blocks, 0)
+
+ counter := uint32(0)
+ firstHeaderWrapper := func(hash common.Hash) error {
+ atomic.AddUint32(&counter, 1)
+ return firstHeaderFetcher(hash)
+ }
+ secondHeaderWrapper := func(hash common.Hash) error {
+ atomic.AddUint32(&counter, 1)
+ return secondHeaderFetcher(hash)
+ }
+ // Iteratively announce blocks until all are imported
+ imported := make(chan interface{})
+ tester.fetcher.importedHook = func(header *types.Header, block *types.Block) {
+ if light {
+ if header == nil {
+ t.Fatalf("Fetcher try to import empty header")
+ }
+ imported <- header
+ } else {
+ if block == nil {
+ t.Fatalf("Fetcher try to import empty block")
+ }
+ imported <- block
+ }
+ }
+ for i := len(hashes) - 2; i >= 0; i-- {
+ tester.fetcher.Notify("first", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), firstHeaderWrapper, firstBodyFetcher)
+ tester.fetcher.Notify("second", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout+time.Millisecond), secondHeaderWrapper, secondBodyFetcher)
+ tester.fetcher.Notify("second", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout-time.Millisecond), secondHeaderWrapper, secondBodyFetcher)
+ verifyImportEvent(t, imported, true)
+ }
+ verifyImportDone(t, imported)
+
+ // Make sure no blocks were retrieved twice
+ if int(counter) != targetBlocks {
+ t.Fatalf("retrieval count mismatch: have %v, want %v", counter, targetBlocks)
+ }
+ verifyChainHeight(t, tester, uint64(len(hashes)-1))
+}
+
+// Tests that announcements arriving while a previous is being fetched still
+// results in a valid import.
+func TestFullOverlappingAnnouncements(t *testing.T) { testOverlappingAnnouncements(t, false) }
+func TestLightOverlappingAnnouncements(t *testing.T) { testOverlappingAnnouncements(t, true) }
+
+func testOverlappingAnnouncements(t *testing.T, light bool) {
+ // Create a chain of blocks to import
+ targetBlocks := 4 * hashLimit
+ hashes, blocks := makeChain(targetBlocks, 0, genesis)
+
+ tester := newTester(light)
+ headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack)
+ bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0)
+
+ // Iteratively announce blocks, but overlap them continuously
+ overlap := 16
+ imported := make(chan interface{}, len(hashes)-1)
+ for i := 0; i < overlap; i++ {
+ imported <- nil
+ }
+ tester.fetcher.importedHook = func(header *types.Header, block *types.Block) {
+ if light {
+ if header == nil {
+ t.Fatalf("Fetcher try to import empty header")
+ }
+ imported <- header
+ } else {
+ if block == nil {
+ t.Fatalf("Fetcher try to import empty block")
+ }
+ imported <- block
+ }
+ }
+
+ for i := len(hashes) - 2; i >= 0; i-- {
+ tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
+ select {
+ case <-imported:
+ case <-time.After(time.Second):
+ t.Fatalf("block %d: import timeout", len(hashes)-i)
+ }
+ }
+ // Wait for all the imports to complete and check count
+ verifyImportCount(t, imported, overlap)
+ verifyChainHeight(t, tester, uint64(len(hashes)-1))
+}
+
+// Tests that announces already being retrieved will not be duplicated.
+func TestFullPendingDeduplication(t *testing.T) { testPendingDeduplication(t, false) }
+func TestLightPendingDeduplication(t *testing.T) { testPendingDeduplication(t, true) }
+
+func testPendingDeduplication(t *testing.T, light bool) {
+ // Create a hash and corresponding block
+ hashes, blocks := makeChain(1, 0, genesis)
+
+ // Assemble a tester with a built in counter and delayed fetcher
+ tester := newTester(light)
+ headerFetcher := tester.makeHeaderFetcher("repeater", blocks, -gatherSlack)
+ bodyFetcher := tester.makeBodyFetcher("repeater", blocks, 0)
+
+ delay := 50 * time.Millisecond
+ counter := uint32(0)
+ headerWrapper := func(hash common.Hash) error {
+ atomic.AddUint32(&counter, 1)
+
+ // Simulate a long running fetch
+ go func() {
+ time.Sleep(delay)
+ headerFetcher(hash)
+ }()
+ return nil
+ }
+ checkNonExist := func() bool {
+ return tester.getBlock(hashes[0]) == nil
+ }
+ if light {
+ checkNonExist = func() bool {
+ return tester.getHeader(hashes[0]) == nil
+ }
+ }
+ // Announce the same block many times until it's fetched (wait for any pending ops)
+ for checkNonExist() {
+ tester.fetcher.Notify("repeater", hashes[0], 1, time.Now().Add(-arriveTimeout), headerWrapper, bodyFetcher)
+ time.Sleep(time.Millisecond)
+ }
+ time.Sleep(delay)
+
+ // Check that all blocks were imported and none fetched twice
+ if int(counter) != 1 {
+ t.Fatalf("retrieval count mismatch: have %v, want %v", counter, 1)
+ }
+ verifyChainHeight(t, tester, 1)
+}
+
+// Tests that announcements retrieved in a random order are cached and eventually
+// imported when all the gaps are filled in.
+func TestFullRandomArrivalImport(t *testing.T) { testRandomArrivalImport(t, false) }
+func TestLightRandomArrivalImport(t *testing.T) { testRandomArrivalImport(t, true) }
+
+func testRandomArrivalImport(t *testing.T, light bool) {
+ // Create a chain of blocks to import, and choose one to delay
+ targetBlocks := maxQueueDist
+ hashes, blocks := makeChain(targetBlocks, 0, genesis)
+ skip := targetBlocks / 2
+
+ tester := newTester(light)
+ headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack)
+ bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0)
+
+ // Iteratively announce blocks, skipping one entry
+ imported := make(chan interface{}, len(hashes)-1)
+ tester.fetcher.importedHook = func(header *types.Header, block *types.Block) {
+ if light {
+ if header == nil {
+ t.Fatalf("Fetcher try to import empty header")
+ }
+ imported <- header
+ } else {
+ if block == nil {
+ t.Fatalf("Fetcher try to import empty block")
+ }
+ imported <- block
+ }
+ }
+ for i := len(hashes) - 1; i >= 0; i-- {
+ if i != skip {
+ tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
+ time.Sleep(time.Millisecond)
+ }
+ }
+ // Finally announce the skipped entry and check full import
+ tester.fetcher.Notify("valid", hashes[skip], uint64(len(hashes)-skip-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
+ verifyImportCount(t, imported, len(hashes)-1)
+ verifyChainHeight(t, tester, uint64(len(hashes)-1))
+}
+
+// Tests that direct block enqueues (due to block propagation vs. hash announce)
+// are correctly schedule, filling and import queue gaps.
+func TestQueueGapFill(t *testing.T) {
+ // Create a chain of blocks to import, and choose one to not announce at all
+ targetBlocks := maxQueueDist
+ hashes, blocks := makeChain(targetBlocks, 0, genesis)
+ skip := targetBlocks / 2
+
+ tester := newTester(false)
+ headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack)
+ bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0)
+
+ // Iteratively announce blocks, skipping one entry
+ imported := make(chan interface{}, len(hashes)-1)
+ tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { imported <- block }
+
+ for i := len(hashes) - 1; i >= 0; i-- {
+ if i != skip {
+ tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
+ time.Sleep(time.Millisecond)
+ }
+ }
+ // Fill the missing block directly as if propagated
+ tester.fetcher.Enqueue("valid", blocks[hashes[skip]])
+ verifyImportCount(t, imported, len(hashes)-1)
+ verifyChainHeight(t, tester, uint64(len(hashes)-1))
+}
+
+// Tests that blocks arriving from various sources (multiple propagations, hash
+// announces, etc) do not get scheduled for import multiple times.
+func TestImportDeduplication(t *testing.T) {
+ // Create two blocks to import (one for duplication, the other for stalling)
+ hashes, blocks := makeChain(2, 0, genesis)
+
+ // Create the tester and wrap the importer with a counter
+ tester := newTester(false)
+ headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack)
+ bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0)
+
+ counter := uint32(0)
+ tester.fetcher.insertChain = func(blocks types.Blocks) (int, error) {
+ atomic.AddUint32(&counter, uint32(len(blocks)))
+ return tester.insertChain(blocks)
+ }
+ // Instrument the fetching and imported events
+ fetching := make(chan []common.Hash)
+ imported := make(chan interface{}, len(hashes)-1)
+ tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- hashes }
+ tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { imported <- block }
+
+ // Announce the duplicating block, wait for retrieval, and also propagate directly
+ tester.fetcher.Notify("valid", hashes[0], 1, time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
+ <-fetching
+
+ tester.fetcher.Enqueue("valid", blocks[hashes[0]])
+ tester.fetcher.Enqueue("valid", blocks[hashes[0]])
+ tester.fetcher.Enqueue("valid", blocks[hashes[0]])
+
+ // Fill the missing block directly as if propagated, and check import uniqueness
+ tester.fetcher.Enqueue("valid", blocks[hashes[1]])
+ verifyImportCount(t, imported, 2)
+
+ if counter != 2 {
+ t.Fatalf("import invocation count mismatch: have %v, want %v", counter, 2)
+ }
+}
+
+// Tests that blocks with numbers much lower or higher than out current head get
+// discarded to prevent wasting resources on useless blocks from faulty peers.
+func TestDistantPropagationDiscarding(t *testing.T) {
+ // Create a long chain to import and define the discard boundaries
+ hashes, blocks := makeChain(3*maxQueueDist, 0, genesis)
+ head := hashes[len(hashes)/2]
+
+ low, high := len(hashes)/2+maxUncleDist+1, len(hashes)/2-maxQueueDist-1
+
+ // Create a tester and simulate a head block being the middle of the above chain
+ tester := newTester(false)
+
+ tester.lock.Lock()
+ tester.hashes = []common.Hash{head}
+ tester.blocks = map[common.Hash]*types.Block{head: blocks[head]}
+ tester.lock.Unlock()
+
+ // Ensure that a block with a lower number than the threshold is discarded
+ tester.fetcher.Enqueue("lower", blocks[hashes[low]])
+ time.Sleep(10 * time.Millisecond)
+ if !tester.fetcher.queue.Empty() {
+ t.Fatalf("fetcher queued stale block")
+ }
+ // Ensure that a block with a higher number than the threshold is discarded
+ tester.fetcher.Enqueue("higher", blocks[hashes[high]])
+ time.Sleep(10 * time.Millisecond)
+ if !tester.fetcher.queue.Empty() {
+ t.Fatalf("fetcher queued future block")
+ }
+}
+
+// Tests that announcements with numbers much lower or higher than out current
+// head get discarded to prevent wasting resources on useless blocks from faulty
+// peers.
+func TestFullDistantAnnouncementDiscarding(t *testing.T) { testDistantAnnouncementDiscarding(t, false) }
+func TestLightDistantAnnouncementDiscarding(t *testing.T) { testDistantAnnouncementDiscarding(t, true) }
+
+func testDistantAnnouncementDiscarding(t *testing.T, light bool) {
+ // Create a long chain to import and define the discard boundaries
+ hashes, blocks := makeChain(3*maxQueueDist, 0, genesis)
+ head := hashes[len(hashes)/2]
+
+ low, high := len(hashes)/2+maxUncleDist+1, len(hashes)/2-maxQueueDist-1
+
+ // Create a tester and simulate a head block being the middle of the above chain
+ tester := newTester(light)
+
+ tester.lock.Lock()
+ tester.hashes = []common.Hash{head}
+ tester.headers = map[common.Hash]*types.Header{head: blocks[head].Header()}
+ tester.blocks = map[common.Hash]*types.Block{head: blocks[head]}
+ tester.lock.Unlock()
+
+ headerFetcher := tester.makeHeaderFetcher("lower", blocks, -gatherSlack)
+ bodyFetcher := tester.makeBodyFetcher("lower", blocks, 0)
+
+ fetching := make(chan struct{}, 2)
+ tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- struct{}{} }
+
+ // Ensure that a block with a lower number than the threshold is discarded
+ tester.fetcher.Notify("lower", hashes[low], blocks[hashes[low]].NumberU64(), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
+ select {
+ case <-time.After(50 * time.Millisecond):
+ case <-fetching:
+ t.Fatalf("fetcher requested stale header")
+ }
+ // Ensure that a block with a higher number than the threshold is discarded
+ tester.fetcher.Notify("higher", hashes[high], blocks[hashes[high]].NumberU64(), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
+ select {
+ case <-time.After(50 * time.Millisecond):
+ case <-fetching:
+ t.Fatalf("fetcher requested future header")
+ }
+}
+
+// Tests that peers announcing blocks with invalid numbers (i.e. not matching
+// the headers provided afterwards) get dropped as malicious.
+func TestFullInvalidNumberAnnouncement(t *testing.T) { testInvalidNumberAnnouncement(t, false) }
+func TestLightInvalidNumberAnnouncement(t *testing.T) { testInvalidNumberAnnouncement(t, true) }
+
+func testInvalidNumberAnnouncement(t *testing.T, light bool) {
+ // Create a single block to import and check numbers against
+ hashes, blocks := makeChain(1, 0, genesis)
+
+ tester := newTester(light)
+ badHeaderFetcher := tester.makeHeaderFetcher("bad", blocks, -gatherSlack)
+ badBodyFetcher := tester.makeBodyFetcher("bad", blocks, 0)
+
+ imported := make(chan interface{})
+ announced := make(chan interface{})
+ tester.fetcher.importedHook = func(header *types.Header, block *types.Block) {
+ if light {
+ if header == nil {
+ t.Fatalf("Fetcher try to import empty header")
+ }
+ imported <- header
+ } else {
+ if block == nil {
+ t.Fatalf("Fetcher try to import empty block")
+ }
+ imported <- block
+ }
+ }
+ // Announce a block with a bad number, check for immediate drop
+ tester.fetcher.announceChangeHook = func(hash common.Hash, b bool) {
+ announced <- nil
+ }
+ tester.fetcher.Notify("bad", hashes[0], 2, time.Now().Add(-arriveTimeout), badHeaderFetcher, badBodyFetcher)
+ verifyAnnounce := func() {
+ for i := 0; i < 2; i++ {
+ select {
+ case <-announced:
+ continue
+ case <-time.After(1 * time.Second):
+ t.Fatal("announce timeout")
+ return
+ }
+ }
+ }
+ verifyAnnounce()
+ verifyImportEvent(t, imported, false)
+ tester.lock.RLock()
+ dropped := tester.drops["bad"]
+ tester.lock.RUnlock()
+
+ if !dropped {
+ t.Fatalf("peer with invalid numbered announcement not dropped")
+ }
+ goodHeaderFetcher := tester.makeHeaderFetcher("good", blocks, -gatherSlack)
+ goodBodyFetcher := tester.makeBodyFetcher("good", blocks, 0)
+ // Make sure a good announcement passes without a drop
+ tester.fetcher.Notify("good", hashes[0], 1, time.Now().Add(-arriveTimeout), goodHeaderFetcher, goodBodyFetcher)
+ verifyAnnounce()
+ verifyImportEvent(t, imported, true)
+
+ tester.lock.RLock()
+ dropped = tester.drops["good"]
+ tester.lock.RUnlock()
+
+ if dropped {
+ t.Fatalf("peer with valid numbered announcement dropped")
+ }
+ verifyImportDone(t, imported)
+}
+
+// Tests that if a block is empty (i.e. header only), no body request should be
+// made, and instead the header should be assembled into a whole block in itself.
+func TestEmptyBlockShortCircuit(t *testing.T) {
+ // Create a chain of blocks to import
+ hashes, blocks := makeChain(32, 0, genesis)
+
+ tester := newTester(false)
+ headerFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack)
+ bodyFetcher := tester.makeBodyFetcher("valid", blocks, 0)
+
+ // Add a monitoring hook for all internal events
+ fetching := make(chan []common.Hash)
+ tester.fetcher.fetchingHook = func(hashes []common.Hash) { fetching <- hashes }
+
+ completing := make(chan []common.Hash)
+ tester.fetcher.completingHook = func(hashes []common.Hash) { completing <- hashes }
+
+ imported := make(chan interface{})
+ tester.fetcher.importedHook = func(header *types.Header, block *types.Block) {
+ if block == nil {
+ t.Fatalf("Fetcher try to import empty block")
+ }
+ imported <- block
+ }
+ // Iteratively announce blocks until all are imported
+ for i := len(hashes) - 2; i >= 0; i-- {
+ tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), headerFetcher, bodyFetcher)
+
+ // All announces should fetch the header
+ verifyFetchingEvent(t, fetching, true)
+
+ // Only blocks with data contents should request bodies
+ verifyCompletingEvent(t, completing, len(blocks[hashes[i]].Transactions()) > 0 || len(blocks[hashes[i]].Uncles()) > 0)
+
+ // Irrelevant of the construct, import should succeed
+ verifyImportEvent(t, imported, true)
+ }
+ verifyImportDone(t, imported)
+}
+
+// Tests that a peer is unable to use unbounded memory with sending infinite
+// block announcements to a node, but that even in the face of such an attack,
+// the fetcher remains operational.
+func TestHashMemoryExhaustionAttack(t *testing.T) {
+ // Create a tester with instrumented import hooks
+ tester := newTester(false)
+
+ imported, announces := make(chan interface{}), int32(0)
+ tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { imported <- block }
+ tester.fetcher.announceChangeHook = func(hash common.Hash, added bool) {
+ if added {
+ atomic.AddInt32(&announces, 1)
+ } else {
+ atomic.AddInt32(&announces, -1)
+ }
+ }
+ // Create a valid chain and an infinite junk chain
+ targetBlocks := hashLimit + 2*maxQueueDist
+ hashes, blocks := makeChain(targetBlocks, 0, genesis)
+ validHeaderFetcher := tester.makeHeaderFetcher("valid", blocks, -gatherSlack)
+ validBodyFetcher := tester.makeBodyFetcher("valid", blocks, 0)
+
+ attack, _ := makeChain(targetBlocks, 0, unknownBlock)
+ attackerHeaderFetcher := tester.makeHeaderFetcher("attacker", nil, -gatherSlack)
+ attackerBodyFetcher := tester.makeBodyFetcher("attacker", nil, 0)
+
+ // Feed the tester a huge hashset from the attacker, and a limited from the valid peer
+ for i := 0; i < len(attack); i++ {
+ if i < maxQueueDist {
+ tester.fetcher.Notify("valid", hashes[len(hashes)-2-i], uint64(i+1), time.Now(), validHeaderFetcher, validBodyFetcher)
+ }
+ tester.fetcher.Notify("attacker", attack[i], 1 /* don't distance drop */, time.Now(), attackerHeaderFetcher, attackerBodyFetcher)
+ }
+ if count := atomic.LoadInt32(&announces); count != hashLimit+maxQueueDist {
+ t.Fatalf("queued announce count mismatch: have %d, want %d", count, hashLimit+maxQueueDist)
+ }
+ // Wait for fetches to complete
+ verifyImportCount(t, imported, maxQueueDist)
+
+ // Feed the remaining valid hashes to ensure DOS protection state remains clean
+ for i := len(hashes) - maxQueueDist - 2; i >= 0; i-- {
+ tester.fetcher.Notify("valid", hashes[i], uint64(len(hashes)-i-1), time.Now().Add(-arriveTimeout), validHeaderFetcher, validBodyFetcher)
+ verifyImportEvent(t, imported, true)
+ }
+ verifyImportDone(t, imported)
+}
+
+// Tests that blocks sent to the fetcher (either through propagation or via hash
+// announces and retrievals) don't pile up indefinitely, exhausting available
+// system memory.
+func TestBlockMemoryExhaustionAttack(t *testing.T) {
+ // Create a tester with instrumented import hooks
+ tester := newTester(false)
+
+ imported, enqueued := make(chan interface{}), int32(0)
+ tester.fetcher.importedHook = func(header *types.Header, block *types.Block) { imported <- block }
+ tester.fetcher.queueChangeHook = func(hash common.Hash, added bool) {
+ if added {
+ atomic.AddInt32(&enqueued, 1)
+ } else {
+ atomic.AddInt32(&enqueued, -1)
+ }
+ }
+ // Create a valid chain and a batch of dangling (but in range) blocks
+ targetBlocks := hashLimit + 2*maxQueueDist
+ hashes, blocks := makeChain(targetBlocks, 0, genesis)
+ attack := make(map[common.Hash]*types.Block)
+ for i := byte(0); len(attack) < blockLimit+2*maxQueueDist; i++ {
+ hashes, blocks := makeChain(maxQueueDist-1, i, unknownBlock)
+ for _, hash := range hashes[:maxQueueDist-2] {
+ attack[hash] = blocks[hash]
+ }
+ }
+ // Try to feed all the attacker blocks make sure only a limited batch is accepted
+ for _, block := range attack {
+ tester.fetcher.Enqueue("attacker", block)
+ }
+ time.Sleep(200 * time.Millisecond)
+ if queued := atomic.LoadInt32(&enqueued); queued != blockLimit {
+ t.Fatalf("queued block count mismatch: have %d, want %d", queued, blockLimit)
+ }
+ // Queue up a batch of valid blocks, and check that a new peer is allowed to do so
+ for i := 0; i < maxQueueDist-1; i++ {
+ tester.fetcher.Enqueue("valid", blocks[hashes[len(hashes)-3-i]])
+ }
+ time.Sleep(100 * time.Millisecond)
+ if queued := atomic.LoadInt32(&enqueued); queued != blockLimit+maxQueueDist-1 {
+ t.Fatalf("queued block count mismatch: have %d, want %d", queued, blockLimit+maxQueueDist-1)
+ }
+ // Insert the missing piece (and sanity check the import)
+ tester.fetcher.Enqueue("valid", blocks[hashes[len(hashes)-2]])
+ verifyImportCount(t, imported, maxQueueDist)
+
+ // Insert the remaining blocks in chunks to ensure clean DOS protection
+ for i := maxQueueDist; i < len(hashes)-1; i++ {
+ tester.fetcher.Enqueue("valid", blocks[hashes[len(hashes)-2-i]])
+ verifyImportEvent(t, imported, true)
+ }
+ verifyImportDone(t, imported)
+}
diff --git a/les/fetcher_test.go b/les/fetcher_test.go
index d3a74d25c2..a922ab0f83 100644
--- a/les/fetcher_test.go
+++ b/les/fetcher_test.go
@@ -26,6 +26,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/p2p/enode"
+ "github.com/ethereum/go-ethereum/params"
)
// verifyImportEvent verifies that one single event arrive on an import channel.
@@ -74,14 +75,12 @@ func testSequentialAnnouncements(t *testing.T, protocol int) {
s, c, teardown := newClientServerEnv(t, netconfig)
defer teardown()
- // Create connected peer pair.
- c.handler.fetcher.noAnnounce = true // Ignore the first announce from peer which can trigger a resync.
- p1, _, err := newTestPeerPair("peer", protocol, s.handler, c.handler)
+ // Create connected peer pair, the initial signal from LES server
+ // is discarded to prevent syncing.
+ p1, _, err := newTestPeerPair("peer", protocol, s.handler, c.handler, true)
if err != nil {
t.Fatalf("Failed to create peer pair %v", err)
}
- c.handler.fetcher.noAnnounce = false
-
importCh := make(chan interface{})
c.handler.fetcher.newHeadHook = func(header *types.Header) {
importCh <- header
@@ -114,14 +113,12 @@ func testGappedAnnouncements(t *testing.T, protocol int) {
s, c, teardown := newClientServerEnv(t, netconfig)
defer teardown()
- // Create connected peer pair.
- c.handler.fetcher.noAnnounce = true // Ignore the first announce from peer which can trigger a resync.
- peer, _, err := newTestPeerPair("peer", protocol, s.handler, c.handler)
+ // Create connected peer pair, the initial signal from LES server
+ // is discarded to prevent syncing.
+ peer, _, err := newTestPeerPair("peer", protocol, s.handler, c.handler, true)
if err != nil {
t.Fatalf("Failed to create peer pair %v", err)
}
- c.handler.fetcher.noAnnounce = false
-
done := make(chan *types.Header, 1)
c.handler.fetcher.newHeadHook = func(header *types.Header) { done <- header }
@@ -141,29 +138,11 @@ func testGappedAnnouncements(t *testing.T, protocol int) {
verifyChainHeight(t, c.handler.fetcher, 4)
// Send a reorged announcement
- var newAnno = make(chan struct{}, 1)
- c.handler.fetcher.noAnnounce = true
- c.handler.fetcher.newAnnounce = func(*serverPeer, *announceData) {
- newAnno <- struct{}{}
- }
blocks, _ := core.GenerateChain(rawdb.ReadChainConfig(s.db, s.backend.Blockchain().Genesis().Hash()), s.backend.Blockchain().GetBlockByNumber(3),
ethash.NewFaker(), s.db, 2, func(i int, gen *core.BlockGen) {
gen.OffsetTime(-9) // higher block difficulty
})
s.backend.Blockchain().InsertChain(blocks)
- <-newAnno
- c.handler.fetcher.noAnnounce = false
- c.handler.fetcher.newAnnounce = nil
-
- latest = blocks[len(blocks)-1].Header()
- hash, number = latest.Hash(), latest.Number.Uint64()
- td = rawdb.ReadTd(s.db, hash, number)
-
- announce = announceData{hash, number, td, 1, nil}
- if peer.cpeer.announceType == announceTypeSigned {
- announce.sign(s.handler.server.privateKey)
- }
- peer.cpeer.sendAnnounce(announce)
<-done // Wait syncing
verifyChainHeight(t, c.handler.fetcher, 5)
@@ -206,20 +185,15 @@ func testTrustedAnnouncement(t *testing.T, protocol int) {
teardowns[i]()
}
}()
-
- c.handler.fetcher.noAnnounce = true // Ignore the first announce from peer which can trigger a resync.
-
// Connect all server instances.
for i := 0; i < len(servers); i++ {
- sp, cp, err := connect(servers[i].handler, nodes[i].ID(), c.handler, protocol)
+ sp, cp, err := connect(servers[i].handler, nodes[i].ID(), c.handler, protocol, true)
if err != nil {
t.Fatalf("connect server and client failed, err %s", err)
}
cpeers = append(cpeers, cp)
speers = append(speers, sp)
}
- c.handler.fetcher.noAnnounce = false
-
newHead := make(chan *types.Header, 1)
c.handler.fetcher.newHeadHook = func(header *types.Header) { newHead <- header }
@@ -262,21 +236,19 @@ func testInvalidAnnounces(t *testing.T, protocol int) {
s, c, teardown := newClientServerEnv(t, netconfig)
defer teardown()
- // Create connected peer pair.
- c.handler.fetcher.noAnnounce = true // Ignore the first announce from peer which can trigger a resync.
- peer, _, err := newTestPeerPair("peer", lpv3, s.handler, c.handler)
+ // Create connected peer pair, the initial signal from LES server
+ // is discarded to prevent syncing.
+ peer, _, err := newTestPeerPair("peer", lpv3, s.handler, c.handler, true)
if err != nil {
t.Fatalf("Failed to create peer pair %v", err)
}
- c.handler.fetcher.noAnnounce = false
-
done := make(chan *types.Header, 1)
c.handler.fetcher.newHeadHook = func(header *types.Header) { done <- header }
// Prepare announcement by latest header.
headerOne := s.backend.Blockchain().GetHeaderByNumber(1)
hash, number := headerOne.Hash(), headerOne.Number.Uint64()
- td := big.NewInt(200) // bad td
+ td := big.NewInt(params.GenesisDifficulty.Int64() + 200) // bad td
// Sign the announcement if necessary.
announce := announceData{hash, number, td, 0, nil}
diff --git a/les/handler_test.go b/les/handler_test.go
index bb8ad33829..aba45764b3 100644
--- a/les/handler_test.go
+++ b/les/handler_test.go
@@ -30,7 +30,7 @@ import (
"github.com/ethereum/go-ethereum/core/rawdb"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
- "github.com/ethereum/go-ethereum/eth/downloader"
+ "github.com/ethereum/go-ethereum/les/downloader"
"github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/p2p"
"github.com/ethereum/go-ethereum/params"
diff --git a/les/odr_test.go b/les/odr_test.go
index ea88495d19..ad77abf5b9 100644
--- a/les/odr_test.go
+++ b/les/odr_test.go
@@ -401,9 +401,9 @@ func testGetTxStatusFromUnindexedPeers(t *testing.T, protocol int) {
closeFns = append(closeFns, closePeer)
// Create a one-time routine for serving message
- go func(i int, peer *testPeer) {
- serveMsg(peer, testspec.txLookups[i])
- }(i, peer)
+ go func(i int, peer *testPeer, lookup uint64) {
+ serveMsg(peer, lookup)
+ }(i, peer, testspec.txLookups[i])
}
// Send out the GetTxStatus requests, compare the result with
diff --git a/les/server_handler.go b/les/server_handler.go
index 80fcf1c44e..f36a87a513 100644
--- a/les/server_handler.go
+++ b/les/server_handler.go
@@ -27,7 +27,6 @@ import (
"github.com/ethereum/go-ethereum/core"
"github.com/ethereum/go-ethereum/core/forkid"
"github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/core/state"
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/les/flowcontrol"
@@ -359,20 +358,20 @@ func (h *serverHandler) AddTxsSync() bool {
}
// getAccount retrieves an account from the state based on root.
-func getAccount(triedb *trie.Database, root, hash common.Hash) (state.Account, error) {
+func getAccount(triedb *trie.Database, root, hash common.Hash) (types.StateAccount, error) {
trie, err := trie.New(root, triedb)
if err != nil {
- return state.Account{}, err
+ return types.StateAccount{}, err
}
blob, err := trie.TryGet(hash[:])
if err != nil {
- return state.Account{}, err
+ return types.StateAccount{}, err
}
- var account state.Account
- if err = rlp.DecodeBytes(blob, &account); err != nil {
- return state.Account{}, err
+ var acc types.StateAccount
+ if err = rlp.DecodeBytes(blob, &acc); err != nil {
+ return types.StateAccount{}, err
}
- return account, nil
+ return acc, nil
}
// getHelperTrie returns the post-processed trie root for the given trie ID and section index
@@ -408,7 +407,7 @@ func (h *serverHandler) broadcastLoop() {
defer headSub.Unsubscribe()
var (
- lastHead *types.Header
+ lastHead = h.blockchain.CurrentHeader()
lastTd = common.Big0
)
for {
diff --git a/les/sync.go b/les/sync.go
index fa5ef4ff82..31cd06ca70 100644
--- a/les/sync.go
+++ b/les/sync.go
@@ -23,7 +23,7 @@ import (
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/core/rawdb"
- "github.com/ethereum/go-ethereum/eth/downloader"
+ "github.com/ethereum/go-ethereum/les/downloader"
"github.com/ethereum/go-ethereum/light"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/params"
diff --git a/les/sync_test.go b/les/sync_test.go
index d3bb90df02..3fc2a9c154 100644
--- a/les/sync_test.go
+++ b/les/sync_test.go
@@ -116,7 +116,7 @@ func testCheckpointSyncing(t *testing.T, protocol int, syncMode int) {
}
// Create connected peer pair.
- peer1, peer2, err := newTestPeerPair("peer", protocol, server.handler, client.handler)
+ peer1, peer2, err := newTestPeerPair("peer", protocol, server.handler, client.handler, false)
if err != nil {
t.Fatalf("Failed to connect testing peers %v", err)
}
@@ -218,7 +218,7 @@ func testMissOracleBackend(t *testing.T, hasCheckpoint bool, protocol int) {
}
}
// Create connected peer pair.
- if _, _, err := newTestPeerPair("peer", 2, server.handler, client.handler); err != nil {
+ if _, _, err := newTestPeerPair("peer", 2, server.handler, client.handler, false); err != nil {
t.Fatalf("Failed to connect testing peers %v", err)
}
select {
@@ -291,7 +291,7 @@ func testSyncFromConfiguredCheckpoint(t *testing.T, protocol int) {
}
}
// Create connected peer pair.
- if _, _, err := newTestPeerPair("peer", 2, server.handler, client.handler); err != nil {
+ if _, _, err := newTestPeerPair("peer", 2, server.handler, client.handler, false); err != nil {
t.Fatalf("Failed to connect testing peers %v", err)
}
@@ -364,7 +364,7 @@ func testSyncAll(t *testing.T, protocol int) {
}
}
// Create connected peer pair.
- if _, _, err := newTestPeerPair("peer", 2, server.handler, client.handler); err != nil {
+ if _, _, err := newTestPeerPair("peer", 2, server.handler, client.handler, false); err != nil {
t.Fatalf("Failed to connect testing peers %v", err)
}
diff --git a/les/test_helper.go b/les/test_helper.go
index 9ff2583b97..21d0f191c9 100644
--- a/les/test_helper.go
+++ b/les/test_helper.go
@@ -398,7 +398,7 @@ func (p *testPeer) close() {
p.app.Close()
}
-func newTestPeerPair(name string, version int, server *serverHandler, client *clientHandler) (*testPeer, *testPeer, error) {
+func newTestPeerPair(name string, version int, server *serverHandler, client *clientHandler, noInitAnnounce bool) (*testPeer, *testPeer, error) {
// Create a message pipe to communicate through
app, net := p2p.MsgPipe()
@@ -423,16 +423,16 @@ func newTestPeerPair(name string, version int, server *serverHandler, client *cl
select {
case <-client.closeCh:
errc2 <- p2p.DiscQuitting
- case errc2 <- client.handle(peer2):
+ case errc2 <- client.handle(peer2, noInitAnnounce):
}
}()
// Ensure the connection is established or exits when any error occurs
for {
select {
case err := <-errc1:
- return nil, nil, fmt.Errorf("Failed to establish protocol connection %v", err)
+ return nil, nil, fmt.Errorf("failed to establish protocol connection %v", err)
case err := <-errc2:
- return nil, nil, fmt.Errorf("Failed to establish protocol connection %v", err)
+ return nil, nil, fmt.Errorf("failed to establish protocol connection %v", err)
default:
}
if atomic.LoadUint32(&peer1.serving) == 1 && atomic.LoadUint32(&peer2.serving) == 1 {
@@ -473,7 +473,7 @@ func (client *testClient) newRawPeer(t *testing.T, name string, version int, rec
select {
case <-client.handler.closeCh:
errCh <- p2p.DiscQuitting
- case errCh <- client.handler.handle(peer):
+ case errCh <- client.handler.handle(peer, false):
}
}()
tp := &testPeer{
@@ -623,7 +623,7 @@ func newClientServerEnv(t *testing.T, config testnetConfig) (*testServer, *testC
if config.connect {
done := make(chan struct{})
client.syncEnd = func(_ *types.Header) { close(done) }
- cpeer, speer, err = newTestPeerPair("peer", config.protocol, server, client)
+ cpeer, speer, err = newTestPeerPair("peer", config.protocol, server, client, false)
if err != nil {
t.Fatalf("Failed to connect testing peers %v", err)
}
diff --git a/les/ulc_test.go b/les/ulc_test.go
index d7308fa593..ecef58d979 100644
--- a/les/ulc_test.go
+++ b/les/ulc_test.go
@@ -20,6 +20,7 @@ import (
"crypto/rand"
"fmt"
"net"
+ "sync/atomic"
"testing"
"time"
@@ -65,7 +66,7 @@ func testULCAnnounceThreshold(t *testing.T, protocol int) {
// Connect all servers.
for i := 0; i < len(servers); i++ {
- connect(servers[i].handler, nodes[i].ID(), c.handler, protocol)
+ connect(servers[i].handler, nodes[i].ID(), c.handler, protocol, false)
}
for i := 0; i < len(servers); i++ {
for j := 0; j < testcase.height[i]; j++ {
@@ -86,7 +87,7 @@ func testULCAnnounceThreshold(t *testing.T, protocol int) {
}
}
-func connect(server *serverHandler, serverId enode.ID, client *clientHandler, protocol int) (*serverPeer, *clientPeer, error) {
+func connect(server *serverHandler, serverId enode.ID, client *clientHandler, protocol int, noInitAnnounce bool) (*serverPeer, *clientPeer, error) {
// Create a message pipe to communicate through
app, net := p2p.MsgPipe()
@@ -110,16 +111,22 @@ func connect(server *serverHandler, serverId enode.ID, client *clientHandler, pr
select {
case <-client.closeCh:
errc1 <- p2p.DiscQuitting
- case errc1 <- client.handle(peer1):
+ case errc1 <- client.handle(peer1, noInitAnnounce):
}
}()
-
- select {
- case <-time.After(time.Millisecond * 100):
- case err := <-errc1:
- return nil, nil, fmt.Errorf("peerLight handshake error: %v", err)
- case err := <-errc2:
- return nil, nil, fmt.Errorf("peerFull handshake error: %v", err)
+ // Ensure the connection is established or exits when any error occurs
+ for {
+ select {
+ case err := <-errc1:
+ return nil, nil, fmt.Errorf("failed to establish protocol connection %v", err)
+ case err := <-errc2:
+ return nil, nil, fmt.Errorf("failed to establish protocol connection %v", err)
+ default:
+ }
+ if atomic.LoadUint32(&peer1.serving) == 1 && atomic.LoadUint32(&peer2.serving) == 1 {
+ break
+ }
+ time.Sleep(50 * time.Millisecond)
}
return peer1, peer2, nil
}
diff --git a/les/vflux/server/clientpool.go b/les/vflux/server/clientpool.go
index 351961b74e..87d783ebab 100644
--- a/les/vflux/server/clientpool.go
+++ b/les/vflux/server/clientpool.go
@@ -143,8 +143,10 @@ func NewClientPool(balanceDb ethdb.KeyValueStore, minCap uint64, connectedBias t
if oldState.HasAll(cp.setup.activeFlag) && oldState.HasNone(cp.setup.activeFlag) {
clientDeactivatedMeter.Mark(1)
}
- _, connected := cp.Active()
- totalConnectedGauge.Update(int64(connected))
+ activeCount, activeCap := cp.Active()
+ totalActiveCountGauge.Update(int64(activeCount))
+ totalActiveCapacityGauge.Update(int64(activeCap))
+ totalInactiveCountGauge.Update(int64(cp.Inactive()))
})
return cp
}
diff --git a/les/vflux/server/metrics.go b/les/vflux/server/metrics.go
index 307b8347af..680aebe2ea 100644
--- a/les/vflux/server/metrics.go
+++ b/les/vflux/server/metrics.go
@@ -21,7 +21,9 @@ import (
)
var (
- totalConnectedGauge = metrics.NewRegisteredGauge("vflux/server/totalConnected", nil)
+ totalActiveCapacityGauge = metrics.NewRegisteredGauge("vflux/server/active/capacity", nil)
+ totalActiveCountGauge = metrics.NewRegisteredGauge("vflux/server/active/count", nil)
+ totalInactiveCountGauge = metrics.NewRegisteredGauge("vflux/server/inactive/count", nil)
clientConnectedMeter = metrics.NewRegisteredMeter("vflux/server/clientEvent/connected", nil)
clientActivatedMeter = metrics.NewRegisteredMeter("vflux/server/clientEvent/activated", nil)
diff --git a/les/vflux/server/prioritypool.go b/les/vflux/server/prioritypool.go
index 480f77e6af..059dac0d46 100644
--- a/les/vflux/server/prioritypool.go
+++ b/les/vflux/server/prioritypool.go
@@ -128,7 +128,7 @@ func newPriorityPool(ns *nodestate.NodeStateMachine, setup *serverSetup, clock m
} else {
ns.SetStateSub(node, nodestate.Flags{}, pp.setup.activeFlag.Or(pp.setup.inactiveFlag), 0)
if n, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo); n != nil {
- pp.disconnectedNode(n)
+ pp.disconnectNode(n)
}
ns.SetFieldSub(node, pp.setup.capacityField, nil)
ns.SetFieldSub(node, pp.setup.queueField, nil)
@@ -137,10 +137,10 @@ func newPriorityPool(ns *nodestate.NodeStateMachine, setup *serverSetup, clock m
ns.SubscribeState(pp.setup.activeFlag.Or(pp.setup.inactiveFlag), func(node *enode.Node, oldState, newState nodestate.Flags) {
if c, _ := pp.ns.GetField(node, pp.setup.queueField).(*ppNodeInfo); c != nil {
if oldState.IsEmpty() {
- pp.connectedNode(c)
+ pp.connectNode(c)
}
if newState.IsEmpty() {
- pp.disconnectedNode(c)
+ pp.disconnectNode(c)
}
}
})
@@ -233,6 +233,14 @@ func (pp *priorityPool) Active() (uint64, uint64) {
return pp.activeCount, pp.activeCap
}
+// Inactive returns the number of currently inactive nodes
+func (pp *priorityPool) Inactive() int {
+ pp.lock.Lock()
+ defer pp.lock.Unlock()
+
+ return pp.inactiveQueue.Size()
+}
+
// Limits returns the maximum allowed number and total capacity of active nodes
func (pp *priorityPool) Limits() (uint64, uint64) {
pp.lock.Lock()
@@ -285,9 +293,9 @@ func (pp *priorityPool) inactivePriority(p *ppNodeInfo) int64 {
return p.nodePriority.priority(pp.minCap)
}
-// connectedNode is called when a new node has been added to the pool (inactiveFlag set)
+// connectNode is called when a new node has been added to the pool (inactiveFlag set)
// Note: this function should run inside a NodeStateMachine operation
-func (pp *priorityPool) connectedNode(c *ppNodeInfo) {
+func (pp *priorityPool) connectNode(c *ppNodeInfo) {
pp.lock.Lock()
pp.activeQueue.Refresh()
if c.connected {
@@ -301,10 +309,10 @@ func (pp *priorityPool) connectedNode(c *ppNodeInfo) {
pp.updateFlags(updates)
}
-// disconnectedNode is called when a node has been removed from the pool (both inactiveFlag
+// disconnectNode is called when a node has been removed from the pool (both inactiveFlag
// and activeFlag reset)
// Note: this function should run inside a NodeStateMachine operation
-func (pp *priorityPool) disconnectedNode(c *ppNodeInfo) {
+func (pp *priorityPool) disconnectNode(c *ppNodeInfo) {
pp.lock.Lock()
pp.activeQueue.Refresh()
if !c.connected {
diff --git a/light/lightchain.go b/light/lightchain.go
index ca6fbfac49..c481734ffe 100644
--- a/light/lightchain.go
+++ b/light/lightchain.go
@@ -430,12 +430,6 @@ 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 (lc *LightChain) GetTdByHash(hash common.Hash) *big.Int {
- return lc.hc.GetTdByHash(hash)
-}
-
// GetHeaderByNumberOdr retrieves the total difficult from the database or
// network by hash and number, caching it (associated with its hash) if found.
func (lc *LightChain) GetTdOdr(ctx context.Context, hash common.Hash, number uint64) *big.Int {
@@ -470,12 +464,6 @@ func (bc *LightChain) GetCanonicalHash(number uint64) common.Hash {
return bc.hc.GetCanonicalHash(number)
}
-// GetBlockHashesFromHash retrieves a number of block hashes starting at a given
-// hash, fetching towards the genesis block.
-func (lc *LightChain) GetBlockHashesFromHash(hash common.Hash, max uint64) []common.Hash {
- return lc.hc.GetBlockHashesFromHash(hash, max)
-}
-
// GetAncestor retrieves the Nth ancestor of a given block. It assumes that either the given block or
// a close ancestor of it is canonical. maxNonCanonical points to a downwards counter limiting the
// number of blocks to be individually checked before we reach the canonical chain.
diff --git a/light/lightchain_test.go b/light/lightchain_test.go
index af36ebd96a..8600e56345 100644
--- a/light/lightchain_test.go
+++ b/light/lightchain_test.go
@@ -104,12 +104,13 @@ func testFork(t *testing.T, LightChain *LightChain, i, n int, comparator func(td
}
// Sanity check that the forked chain can be imported into the original
var tdPre, tdPost *big.Int
-
- tdPre = LightChain.GetTdByHash(LightChain.CurrentHeader().Hash())
+ cur := LightChain.CurrentHeader()
+ tdPre = LightChain.GetTd(cur.Hash(), cur.Number.Uint64())
if err := testHeaderChainImport(headerChainB, LightChain); err != nil {
t.Fatalf("failed to import forked header chain: %v", err)
}
- tdPost = LightChain.GetTdByHash(headerChainB[len(headerChainB)-1].Hash())
+ last := headerChainB[len(headerChainB)-1]
+ tdPost = LightChain.GetTd(last.Hash(), last.Number.Uint64())
// Compare the total difficulties of the chains
comparator(tdPre, tdPost)
}
@@ -124,7 +125,8 @@ func testHeaderChainImport(chain []*types.Header, lightchain *LightChain) error
}
// Manually insert the header into the database, but don't reorganize (allows subsequent testing)
lightchain.chainmu.Lock()
- rawdb.WriteTd(lightchain.chainDb, header.Hash(), header.Number.Uint64(), new(big.Int).Add(header.Difficulty, lightchain.GetTdByHash(header.ParentHash)))
+ rawdb.WriteTd(lightchain.chainDb, header.Hash(), header.Number.Uint64(),
+ new(big.Int).Add(header.Difficulty, lightchain.GetTd(header.ParentHash, header.Number.Uint64()-1)))
rawdb.WriteHeader(lightchain.chainDb, header)
lightchain.chainmu.Unlock()
}
@@ -309,7 +311,7 @@ func testReorg(t *testing.T, first, second []int, td int64) {
}
// Make sure the chain total difficulty is the correct one
want := new(big.Int).Add(bc.genesisBlock.Difficulty(), big.NewInt(td))
- if have := bc.GetTdByHash(bc.CurrentHeader().Hash()); have.Cmp(want) != 0 {
+ if have := bc.GetTd(bc.CurrentHeader().Hash(), bc.CurrentHeader().Number.Uint64()); have.Cmp(want) != 0 {
t.Errorf("total difficulty mismatch: have %v, want %v", have, want)
}
}
diff --git a/light/postprocess.go b/light/postprocess.go
index 891c8a5869..ce38d091e8 100644
--- a/light/postprocess.go
+++ b/light/postprocess.go
@@ -217,7 +217,7 @@ func (c *ChtIndexerBackend) Process(ctx context.Context, header *types.Header) e
// Commit implements core.ChainIndexerBackend
func (c *ChtIndexerBackend) Commit() error {
- root, err := c.trie.Commit(nil)
+ root, _, err := c.trie.Commit(nil)
if err != nil {
return err
}
@@ -454,7 +454,7 @@ func (b *BloomTrieIndexerBackend) Commit() error {
b.trie.Delete(encKey[:])
}
}
- root, err := b.trie.Commit(nil)
+ root, _, err := b.trie.Commit(nil)
if err != nil {
return err
}
diff --git a/light/trie.go b/light/trie.go
index 0516b94486..4ab6f4ace0 100644
--- a/light/trie.go
+++ b/light/trie.go
@@ -27,6 +27,7 @@ import (
"github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
+ "github.com/ethereum/go-ethereum/rlp"
"github.com/ethereum/go-ethereum/trie"
)
@@ -111,6 +112,17 @@ func (t *odrTrie) TryGet(key []byte) ([]byte, error) {
return res, err
}
+func (t *odrTrie) TryUpdateAccount(key []byte, acc *types.StateAccount) error {
+ key = crypto.Keccak256(key)
+ value, err := rlp.EncodeToBytes(acc)
+ if err != nil {
+ return fmt.Errorf("decoding error in account update: %w", err)
+ }
+ return t.do(key, func() error {
+ return t.trie.TryUpdate(key, value)
+ })
+}
+
func (t *odrTrie) TryUpdate(key, value []byte) error {
key = crypto.Keccak256(key)
return t.do(key, func() error {
@@ -125,9 +137,9 @@ func (t *odrTrie) TryDelete(key []byte) error {
})
}
-func (t *odrTrie) Commit(onleaf trie.LeafCallback) (common.Hash, error) {
+func (t *odrTrie) Commit(onleaf trie.LeafCallback) (common.Hash, int, error) {
if t.trie == nil {
- return t.id.Root, nil
+ return t.id.Root, 0, nil
}
return t.trie.Commit(onleaf)
}
diff --git a/log/handler_go13.go b/log/handler_go13.go
index 0843ed0e5f..4df694debe 100644
--- a/log/handler_go13.go
+++ b/log/handler_go13.go
@@ -1,3 +1,4 @@
+//go:build !go1.4
// +build !go1.4
package log
diff --git a/log/handler_go14.go b/log/handler_go14.go
index 05dedbf2a7..d0cb14aa06 100644
--- a/log/handler_go14.go
+++ b/log/handler_go14.go
@@ -1,3 +1,4 @@
+//go:build go1.4
// +build go1.4
package log
diff --git a/log/syslog.go b/log/syslog.go
index 71a17b30b3..451d831b6d 100644
--- a/log/syslog.go
+++ b/log/syslog.go
@@ -1,3 +1,4 @@
+//go:build !windows && !plan9
// +build !windows,!plan9
package log
diff --git a/metrics/cpu_disabled.go b/metrics/cpu_disabled.go
index 6c3428993f..025d97aeb3 100644
--- a/metrics/cpu_disabled.go
+++ b/metrics/cpu_disabled.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 ios
+//go:build ios || js
+// +build ios js
package metrics
diff --git a/metrics/cpu_enabled.go b/metrics/cpu_enabled.go
index 02192928b7..533d40b85a 100644
--- a/metrics/cpu_enabled.go
+++ b/metrics/cpu_enabled.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 !ios
+//go:build !ios && !js
+// +build !ios,!js
package metrics
diff --git a/metrics/cpu_windows.go b/metrics/cputime_nop.go
similarity index 95%
rename from metrics/cpu_windows.go
rename to metrics/cputime_nop.go
index fb29a52a82..0188735a78 100644
--- a/metrics/cpu_windows.go
+++ b/metrics/cputime_nop.go
@@ -14,6 +14,9 @@
// You should have received a copy of the GNU Lesser General Public License
// along with the go-ethereum library. If not, see .
+//go:build windows || js
+// +build windows js
+
package metrics
// getProcessCPUTime returns 0 on Windows as there is no system call to resolve
diff --git a/metrics/cpu_syscall.go b/metrics/cputime_unix.go
similarity index 96%
rename from metrics/cpu_syscall.go
rename to metrics/cputime_unix.go
index 50e04ef1d3..3c56a75d00 100644
--- a/metrics/cpu_syscall.go
+++ b/metrics/cputime_unix.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 !windows
+//go:build !windows && !js
+// +build !windows,!js
package metrics
diff --git a/metrics/disk_nop.go b/metrics/disk_nop.go
index 4319f8b277..58fa4e02f8 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
diff --git a/metrics/runtime_cgo.go b/metrics/runtime_cgo.go
index e3391f4e89..4307ebdba6 100644
--- a/metrics/runtime_cgo.go
+++ b/metrics/runtime_cgo.go
@@ -1,5 +1,5 @@
-// +build cgo
-// +build !appengine
+//go:build cgo && !appengine && !js
+// +build cgo,!appengine,!js
package metrics
diff --git a/metrics/runtime_gccpufraction.go b/metrics/runtime_gccpufraction.go
index ca12c05bac..28cd44752b 100644
--- a/metrics/runtime_gccpufraction.go
+++ b/metrics/runtime_gccpufraction.go
@@ -1,3 +1,4 @@
+//go:build go1.5
// +build go1.5
package metrics
diff --git a/metrics/runtime_no_cgo.go b/metrics/runtime_no_cgo.go
index 616a3b4751..1799bef63b 100644
--- a/metrics/runtime_no_cgo.go
+++ b/metrics/runtime_no_cgo.go
@@ -1,4 +1,5 @@
-// +build !cgo appengine
+//go:build !cgo || appengine || js
+// +build !cgo appengine js
package metrics
diff --git a/metrics/runtime_no_gccpufraction.go b/metrics/runtime_no_gccpufraction.go
index be96aa6f1b..af1a4b63c8 100644
--- a/metrics/runtime_no_gccpufraction.go
+++ b/metrics/runtime_no_gccpufraction.go
@@ -1,3 +1,4 @@
+//go:build !go1.5
// +build !go1.5
package metrics
diff --git a/metrics/syslog.go b/metrics/syslog.go
index a0ed4b1b23..551a2bd0f0 100644
--- a/metrics/syslog.go
+++ b/metrics/syslog.go
@@ -1,3 +1,4 @@
+//go:build !windows
// +build !windows
package metrics
diff --git a/miner/miner.go b/miner/miner.go
index a4a01b9f4f..1c33b3bd28 100644
--- a/miner/miner.go
+++ b/miner/miner.go
@@ -20,6 +20,7 @@ package miner
import (
"fmt"
"math/big"
+ "sync"
"time"
"github.com/ethereum/go-ethereum/common"
@@ -63,6 +64,8 @@ type Miner struct {
exitCh chan struct{}
startCh chan common.Address
stopCh chan struct{}
+
+ wg sync.WaitGroup
}
func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *event.TypeMux, engine consensus.Engine, isLocalBlock func(block *types.Block) bool) *Miner {
@@ -75,8 +78,8 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even
stopCh: make(chan struct{}),
worker: newWorker(config, chainConfig, engine, eth, mux, isLocalBlock, true),
}
+ miner.wg.Add(1)
go miner.update()
-
return miner
}
@@ -85,6 +88,8 @@ func New(eth Backend, config *Config, chainConfig *params.ChainConfig, mux *even
// 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 (miner *Miner) update() {
+ defer miner.wg.Done()
+
events := miner.mux.Subscribe(downloader.StartEvent{}, downloader.DoneEvent{}, downloader.FailedEvent{})
defer func() {
if !events.Closed() {
@@ -154,6 +159,7 @@ func (miner *Miner) Stop() {
func (miner *Miner) Close() {
close(miner.exitCh)
+ miner.wg.Wait()
}
func (miner *Miner) Mining() bool {
diff --git a/miner/stress/1559/main.go b/miner/stress/1559/main.go
index 90f210b272..7c6134b3f9 100644
--- a/miner/stress/1559/main.go
+++ b/miner/stress/1559/main.go
@@ -23,10 +23,9 @@ import (
"math/big"
"math/rand"
"os"
- "path/filepath"
+ "os/signal"
"time"
- "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/consensus/ethash"
@@ -58,12 +57,17 @@ func main() {
faucets[i], _ = crypto.GenerateKey()
}
// Pre-generate the ethash mining DAG so we don't race
- ethash.MakeDataset(1, filepath.Join(os.Getenv("HOME"), ".ethash"))
+ ethash.MakeDataset(1, ethconfig.Defaults.Ethash.DatasetDir)
// Create an Ethash network based off of the Ropsten config
genesis := makeGenesis(faucets)
+ // Handle interrupts.
+ interruptCh := make(chan os.Signal, 5)
+ signal.Notify(interruptCh, os.Interrupt)
+
var (
+ stacks []*node.Node
nodes []*eth.Ethereum
enodes []*enode.Node
)
@@ -85,12 +89,6 @@ func main() {
// Start tracking the node and its enode
nodes = append(nodes, ethBackend)
enodes = append(enodes, stack.Server().Self())
-
- // Inject the signer key and start sealing with it
- store := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
- if _, err := store.NewAccount(""); err != nil {
- panic(err)
- }
}
// Iterate over all the nodes and start mining
@@ -111,6 +109,16 @@ func main() {
signer = types.LatestSignerForChainID(genesis.Config.ChainID)
)
for {
+ // Stop when interrupted.
+ select {
+ case <-interruptCh:
+ for _, node := range stacks {
+ node.Close()
+ }
+ return
+ default:
+ }
+
// Pick a random mining node
index := rand.Intn(len(faucets))
backend := nodes[index%len(nodes)]
@@ -242,9 +250,10 @@ func makeMiner(genesis *core.Genesis) (*node.Node, *eth.Ethereum, error) {
GPO: ethconfig.Defaults.GPO,
Ethash: ethconfig.Defaults.Ethash,
Miner: miner.Config{
- GasCeil: genesis.GasLimit * 11 / 10,
- GasPrice: big.NewInt(1),
- Recommit: time.Second,
+ Etherbase: common.Address{1},
+ GasCeil: genesis.GasLimit * 11 / 10,
+ GasPrice: big.NewInt(1),
+ Recommit: time.Second,
},
})
if err != nil {
diff --git a/miner/stress/clique/main.go b/miner/stress/clique/main.go
index 2aad40bb51..f30da55dff 100644
--- a/miner/stress/clique/main.go
+++ b/miner/stress/clique/main.go
@@ -24,6 +24,7 @@ import (
"math/big"
"math/rand"
"os"
+ "os/signal"
"time"
"github.com/ethereum/go-ethereum/accounts/keystore"
@@ -59,11 +60,15 @@ func main() {
// Create a Clique network based off of the Rinkeby config
genesis := makeGenesis(faucets, sealers)
+ // Handle interrupts.
+ interruptCh := make(chan os.Signal, 5)
+ signal.Notify(interruptCh, os.Interrupt)
+
var (
+ stacks []*node.Node
nodes []*eth.Ethereum
enodes []*enode.Node
)
-
for _, sealer := range sealers {
// Start the node and wait until it's up
stack, ethBackend, err := makeSealer(genesis)
@@ -80,18 +85,20 @@ func main() {
stack.Server().AddPeer(n)
}
// Start tracking the node and its enode
+ stacks = append(stacks, stack)
nodes = append(nodes, ethBackend)
enodes = append(enodes, stack.Server().Self())
// Inject the signer key and start sealing with it
- store := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
- signer, err := store.ImportECDSA(sealer, "")
+ ks := keystore.NewKeyStore(stack.KeyStoreDir(), keystore.LightScryptN, keystore.LightScryptP)
+ signer, err := ks.ImportECDSA(sealer, "")
if err != nil {
panic(err)
}
- if err := store.Unlock(signer, ""); err != nil {
+ if err := ks.Unlock(signer, ""); err != nil {
panic(err)
}
+ stack.AccountManager().AddBackend(ks)
}
// Iterate over all the nodes and start signing on them
@@ -106,6 +113,16 @@ func main() {
// Start injecting transactions from the faucet like crazy
nonces := make([]uint64, len(faucets))
for {
+ // Stop when interrupted.
+ select {
+ case <-interruptCh:
+ for _, node := range stacks {
+ node.Close()
+ }
+ return
+ default:
+ }
+
// Pick a random signer node
index := rand.Intn(len(faucets))
backend := nodes[index%len(nodes)]
diff --git a/miner/stress/ethash/main.go b/miner/stress/ethash/main.go
index 7958e9ab8e..bd9111ee35 100644
--- a/miner/stress/ethash/main.go
+++ b/miner/stress/ethash/main.go
@@ -23,10 +23,9 @@ import (
"math/big"
"math/rand"
"os"
- "path/filepath"
+ "os/signal"
"time"
- "github.com/ethereum/go-ethereum/accounts/keystore"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/fdlimit"
"github.com/ethereum/go-ethereum/consensus/ethash"
@@ -54,12 +53,17 @@ func main() {
faucets[i], _ = crypto.GenerateKey()
}
// Pre-generate the ethash mining DAG so we don't race
- ethash.MakeDataset(1, filepath.Join(os.Getenv("HOME"), ".ethash"))
+ ethash.MakeDataset(1, ethconfig.Defaults.Ethash.DatasetDir)
// Create an Ethash network based off of the Ropsten config
genesis := makeGenesis(faucets)
+ // Handle interrupts.
+ interruptCh := make(chan os.Signal, 5)
+ signal.Notify(interruptCh, os.Interrupt)
+
var (
+ stacks []*node.Node
nodes []*eth.Ethereum
enodes []*enode.Node
)
@@ -79,14 +83,9 @@ func main() {
stack.Server().AddPeer(n)
}
// Start tracking the node and its enode
+ stacks = append(stacks, stack)
nodes = append(nodes, ethBackend)
enodes = append(enodes, stack.Server().Self())
-
- // Inject the signer key and start sealing with it
- store := stack.AccountManager().Backends(keystore.KeyStoreType)[0].(*keystore.KeyStore)
- if _, err := store.NewAccount(""); err != nil {
- panic(err)
- }
}
// Iterate over all the nodes and start mining
@@ -101,6 +100,16 @@ func main() {
// Start injecting transactions from the faucets like crazy
nonces := make([]uint64, len(faucets))
for {
+ // Stop when interrupted.
+ select {
+ case <-interruptCh:
+ for _, node := range stacks {
+ node.Close()
+ }
+ return
+ default:
+ }
+
// Pick a random mining node
index := rand.Intn(len(faucets))
backend := nodes[index%len(nodes)]
@@ -171,9 +180,10 @@ func makeMiner(genesis *core.Genesis) (*node.Node, *eth.Ethereum, error) {
GPO: ethconfig.Defaults.GPO,
Ethash: ethconfig.Defaults.Ethash,
Miner: miner.Config{
- GasCeil: genesis.GasLimit * 11 / 10,
- GasPrice: big.NewInt(1),
- Recommit: time.Second,
+ Etherbase: common.Address{1},
+ GasCeil: genesis.GasLimit * 11 / 10,
+ GasPrice: big.NewInt(1),
+ Recommit: time.Second,
},
})
if err != nil {
diff --git a/miner/worker.go b/miner/worker.go
index 8bdb1eff7c..4ef2c8c0da 100644
--- a/miner/worker.go
+++ b/miner/worker.go
@@ -150,6 +150,8 @@ type worker struct {
resubmitIntervalCh chan time.Duration
resubmitAdjustCh chan *intervalAdjust
+ wg sync.WaitGroup
+
current *environment // An environment for current running cycle.
localUncles map[common.Hash]*types.Block // A set of side blocks generated locally as the possible uncle blocks.
remoteUncles map[common.Hash]*types.Block // A set of side blocks as the possible uncle blocks.
@@ -225,6 +227,7 @@ func newWorker(config *Config, chainConfig *params.ChainConfig, engine consensus
recommit = minRecommitInterval
}
+ worker.wg.Add(4)
go worker.mainLoop()
go worker.newWorkLoop(recommit)
go worker.resultLoop()
@@ -318,11 +321,9 @@ func (w *worker) isRunning() bool {
// close terminates all background threads maintained by the worker.
// Note the worker does not support being closed multiple times.
func (w *worker) close() {
- if w.current != nil && w.current.state != nil {
- w.current.state.StopPrefetcher()
- }
atomic.StoreInt32(&w.running, 0)
close(w.exitCh)
+ w.wg.Wait()
}
// recalcRecommit recalculates the resubmitting interval upon feedback.
@@ -349,6 +350,7 @@ func recalcRecommit(minRecommit, prev time.Duration, target float64, inc bool) t
// newWorkLoop is a standalone goroutine to submit new mining work upon received events.
func (w *worker) newWorkLoop(recommit time.Duration) {
+ defer w.wg.Done()
var (
interrupt *int32
minRecommit = recommit // minimal resubmit interval specified by user.
@@ -446,9 +448,15 @@ func (w *worker) newWorkLoop(recommit time.Duration) {
// mainLoop is a standalone goroutine to regenerate the sealing task based on the received event.
func (w *worker) mainLoop() {
+ defer w.wg.Done()
defer w.txsSub.Unsubscribe()
defer w.chainHeadSub.Unsubscribe()
defer w.chainSideSub.Unsubscribe()
+ defer func() {
+ if w.current != nil && w.current.state != nil {
+ w.current.state.StopPrefetcher()
+ }
+ }()
for {
select {
@@ -548,6 +556,7 @@ func (w *worker) mainLoop() {
// taskLoop is a standalone goroutine to fetch sealing task from the generator and
// push them to consensus engine.
func (w *worker) taskLoop() {
+ defer w.wg.Done()
var (
stopCh chan struct{}
prev common.Hash
@@ -595,6 +604,7 @@ func (w *worker) taskLoop() {
// resultLoop is a standalone goroutine to handle sealing result submitting
// and flush relative data to the database.
func (w *worker) resultLoop() {
+ defer w.wg.Done()
for {
select {
case block := <-w.resultCh:
@@ -622,17 +632,23 @@ func (w *worker) resultLoop() {
receipts = make([]*types.Receipt, len(task.receipts))
logs []*types.Log
)
- for i, receipt := range task.receipts {
+ for i, taskReceipt := range task.receipts {
+ receipt := new(types.Receipt)
+ receipts[i] = receipt
+ *receipt = *taskReceipt
+
// add block location fields
receipt.BlockHash = hash
receipt.BlockNumber = block.Number()
receipt.TransactionIndex = uint(i)
- receipts[i] = new(types.Receipt)
- *receipts[i] = *receipt
// Update the block hash in all logs since it is now available and not when the
// receipt/log of individual transactions were created.
- for _, log := range receipt.Logs {
+ receipt.Logs = make([]*types.Log, len(taskReceipt.Logs))
+ for i, taskLog := range taskReceipt.Logs {
+ log := new(types.Log)
+ receipt.Logs[i] = log
+ *log = *taskLog
log.BlockHash = hash
}
logs = append(logs, receipt.Logs...)
@@ -977,11 +993,7 @@ func (w *worker) commitNewWork(interrupt *int32, noempty bool, timestamp int64)
}
// Fill the block with all available pending transactions.
- pending, err := w.eth.TxPool().Pending(true)
- if err != nil {
- log.Error("Failed to fetch pending transactions", "err", err)
- return
- }
+ pending := w.eth.TxPool().Pending(true)
// Short circuit if there is no available pending transactions.
// But if we disable empty precommit already, ignore it. Since
// empty block is necessary to keep the liveness of the network.
diff --git a/miner/worker_test.go b/miner/worker_test.go
index 9c4dc0f378..5b35c66dce 100644
--- a/miner/worker_test.go
+++ b/miner/worker_test.go
@@ -74,8 +74,10 @@ var (
func init() {
testTxPoolConfig = core.DefaultTxPoolConfig
testTxPoolConfig.Journal = ""
- ethashChainConfig = params.TestChainConfig
- cliqueChainConfig = params.TestChainConfig
+ ethashChainConfig = new(params.ChainConfig)
+ *ethashChainConfig = *params.TestChainConfig
+ cliqueChainConfig = new(params.ChainConfig)
+ *cliqueChainConfig = *params.TestChainConfig
cliqueChainConfig.Clique = ¶ms.CliqueConfig{
Period: 10,
Epoch: 30000,
diff --git a/mobile/geth_android.go b/mobile/geth_android.go
index 8e4ebe638f..cfdf1c28c9 100644
--- a/mobile/geth_android.go
+++ b/mobile/geth_android.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 android
// +build android
package geth
diff --git a/mobile/geth_ios.go b/mobile/geth_ios.go
index 307cd08580..aab839727f 100644
--- a/mobile/geth_ios.go
+++ b/mobile/geth_ios.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 ios
// +build ios
package geth
diff --git a/mobile/geth_other.go b/mobile/geth_other.go
index 6f0c5dda68..c5cad4a7ba 100644
--- a/mobile/geth_other.go
+++ b/mobile/geth_other.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 !android && !ios
// +build !android,!ios
package geth
diff --git a/node/api.go b/node/api.go
index be20b89d95..a685ecd6b3 100644
--- a/node/api.go
+++ b/node/api.go
@@ -218,7 +218,7 @@ func (api *privateAdminAPI) StartHTTP(host *string, port *int, cors *string, api
}
// StartRPC starts the HTTP RPC API server.
-// This method is deprecated. Use StartHTTP instead.
+// Deprecated: use StartHTTP instead.
func (api *privateAdminAPI) StartRPC(host *string, port *int, cors *string, apis *string, vhosts *string) (bool, error) {
log.Warn("Deprecation warning", "method", "admin.StartRPC", "use-instead", "admin.StartHTTP")
return api.StartHTTP(host, port, cors, apis, vhosts)
@@ -231,7 +231,7 @@ func (api *privateAdminAPI) StopHTTP() (bool, error) {
}
// StopRPC shuts down the HTTP server.
-// This method is deprecated. Use StopHTTP instead.
+// Deprecated: use StopHTTP instead.
func (api *privateAdminAPI) StopRPC() (bool, error) {
log.Warn("Deprecation warning", "method", "admin.StopRPC", "use-instead", "admin.StopHTTP")
return api.StopHTTP()
diff --git a/node/config.go b/node/config.go
index b7e99eb7bf..26f00cd678 100644
--- a/node/config.go
+++ b/node/config.go
@@ -26,11 +26,6 @@ import (
"strings"
"sync"
- "github.com/ethereum/go-ethereum/accounts"
- "github.com/ethereum/go-ethereum/accounts/external"
- "github.com/ethereum/go-ethereum/accounts/keystore"
- "github.com/ethereum/go-ethereum/accounts/scwallet"
- "github.com/ethereum/go-ethereum/accounts/usbwallet"
"github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
@@ -430,15 +425,8 @@ func (c *Config) parsePersistentNodes(w *bool, path string) []*enode.Node {
return nodes
}
-// AccountConfig determines the settings for scrypt and keydirectory
-func (c *Config) AccountConfig() (int, int, string, error) {
- scryptN := keystore.StandardScryptN
- scryptP := keystore.StandardScryptP
- if c.UseLightweightKDF {
- scryptN = keystore.LightScryptN
- scryptP = keystore.LightScryptP
- }
-
+// KeyDirConfig determines the settings for keydirectory
+func (c *Config) KeyDirConfig() (string, error) {
var (
keydir string
err error
@@ -455,71 +443,31 @@ func (c *Config) AccountConfig() (int, int, string, error) {
case c.KeyStoreDir != "":
keydir, err = filepath.Abs(c.KeyStoreDir)
}
- return scryptN, scryptP, keydir, err
+ return keydir, err
}
-func makeAccountManager(conf *Config) (*accounts.Manager, string, error) {
- scryptN, scryptP, keydir, err := conf.AccountConfig()
- var ephemeral string
+// getKeyStoreDir retrieves the key directory and will create
+// and ephemeral one if necessary.
+func getKeyStoreDir(conf *Config) (string, bool, error) {
+ keydir, err := conf.KeyDirConfig()
+ if err != nil {
+ return "", false, err
+ }
+ isEphemeral := false
if keydir == "" {
// There is no datadir.
keydir, err = ioutil.TempDir("", "go-ethereum-keystore")
- ephemeral = keydir
+ isEphemeral = true
}
if err != nil {
- return nil, "", err
+ return "", false, err
}
if err := os.MkdirAll(keydir, 0700); err != nil {
- return nil, "", err
- }
- // Assemble the account manager and supported backends
- var backends []accounts.Backend
- if len(conf.ExternalSigner) > 0 {
- log.Info("Using external signer", "url", conf.ExternalSigner)
- if extapi, err := external.NewExternalBackend(conf.ExternalSigner); err == nil {
- backends = append(backends, extapi)
- } else {
- return nil, "", fmt.Errorf("error connecting to external signer: %v", err)
- }
- }
- if len(backends) == 0 {
- // For now, we're using EITHER external signer OR local signers.
- // If/when we implement some form of lockfile for USB and keystore wallets,
- // we can have both, but it's very confusing for the user to see the same
- // accounts in both externally and locally, plus very racey.
- backends = append(backends, keystore.NewKeyStore(keydir, scryptN, scryptP))
- if conf.USB {
- // Start a USB hub for Ledger hardware wallets
- if ledgerhub, err := usbwallet.NewLedgerHub(); err != nil {
- log.Warn(fmt.Sprintf("Failed to start Ledger hub, disabling: %v", err))
- } else {
- backends = append(backends, ledgerhub)
- }
- // Start a USB hub for Trezor hardware wallets (HID version)
- if trezorhub, err := usbwallet.NewTrezorHubWithHID(); err != nil {
- log.Warn(fmt.Sprintf("Failed to start HID Trezor hub, disabling: %v", err))
- } else {
- backends = append(backends, trezorhub)
- }
- // Start a USB hub for Trezor hardware wallets (WebUSB version)
- if trezorhub, err := usbwallet.NewTrezorHubWithWebUSB(); err != nil {
- log.Warn(fmt.Sprintf("Failed to start WebUSB Trezor hub, disabling: %v", err))
- } else {
- backends = append(backends, trezorhub)
- }
- }
- if len(conf.SmartCardDaemonPath) > 0 {
- // Start a smart card hub
- if schub, err := scwallet.NewHub(conf.SmartCardDaemonPath, scwallet.Scheme, keydir); err != nil {
- log.Warn(fmt.Sprintf("Failed to start smart card hub, disabling: %v", err))
- } else {
- backends = append(backends, schub)
- }
- }
+ return "", false, err
}
- return accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: conf.InsecureUnlockAllowed}, backends...), ephemeral, nil
+ return keydir, isEphemeral, nil
}
var warnLock sync.Mutex
diff --git a/node/node.go b/node/node.go
index 1e65fff1c2..ceab1c9090 100644
--- a/node/node.go
+++ b/node/node.go
@@ -42,7 +42,8 @@ type Node struct {
config *Config
accman *accounts.Manager
log log.Logger
- ephemKeystore string // if non-empty, the key directory that will be removed by Stop
+ keyDir string // key store directory
+ keyDirTemp bool // If true, key directory will be removed by Stop
dirLock fileutil.Releaser // prevents concurrent use of instance directory
stop chan struct{} // Channel to wait for termination notifications
server *p2p.Server // Currently running P2P networking layer
@@ -112,14 +113,15 @@ func New(conf *Config) (*Node, error) {
if err := node.openDataDir(); err != nil {
return nil, err
}
- // Ensure that the AccountManager method works before the node has started. We rely on
- // this in cmd/geth.
- am, ephemeralKeystore, err := makeAccountManager(conf)
+ keyDir, isEphem, err := getKeyStoreDir(conf)
if err != nil {
return nil, err
}
- node.accman = am
- node.ephemKeystore = ephemeralKeystore
+ node.keyDir = keyDir
+ node.keyDirTemp = isEphem
+ // Creates an empty AccountManager with no backends. Callers (e.g. cmd/geth)
+ // are required to add the backends later on.
+ node.accman = accounts.NewManager(&accounts.Config{InsecureUnlockAllowed: conf.InsecureUnlockAllowed})
// Initialize the p2p server. This creates the node key and discovery databases.
node.server.Config.PrivateKey = node.config.NodeKey()
@@ -233,8 +235,8 @@ func (n *Node) doClose(errs []error) error {
if err := n.accman.Close(); err != nil {
errs = append(errs, err)
}
- if n.ephemKeystore != "" {
- if err := os.RemoveAll(n.ephemKeystore); err != nil {
+ if n.keyDirTemp {
+ if err := os.RemoveAll(n.keyDir); err != nil {
errs = append(errs, err)
}
}
@@ -514,6 +516,11 @@ func (n *Node) InstanceDir() string {
return n.config.instanceDir()
}
+// KeyStoreDir retrieves the key directory
+func (n *Node) KeyStoreDir() string {
+ return n.keyDir
+}
+
// AccountManager retrieves the account manager used by the protocol stack.
func (n *Node) AccountManager() *accounts.Manager {
return n.accman
diff --git a/p2p/discover/v5wire/encoding_test.go b/p2p/discover/v5wire/encoding_test.go
index d9c807e0a8..355a8f6913 100644
--- a/p2p/discover/v5wire/encoding_test.go
+++ b/p2p/discover/v5wire/encoding_test.go
@@ -20,13 +20,13 @@ import (
"bytes"
"crypto/ecdsa"
"encoding/hex"
+ "errors"
"flag"
"fmt"
"io/ioutil"
"net"
"os"
"path/filepath"
- "reflect"
"strings"
"testing"
@@ -512,9 +512,6 @@ func (n *handshakeTestNode) init(key *ecdsa.PrivateKey, ip net.IP, clock mclock.
db, _ := enode.OpenDB("")
n.ln = enode.NewLocalNode(db, key)
n.ln.SetStaticIP(ip)
- if n.ln.Node().Seq() != 1 {
- panic(fmt.Errorf("unexpected seq %d", n.ln.Node().Seq()))
- }
n.c = NewCodec(n.ln, key, clock)
}
@@ -558,7 +555,7 @@ func (n *handshakeTestNode) expectDecode(t *testing.T, ptype byte, p []byte) Pac
func (n *handshakeTestNode) expectDecodeErr(t *testing.T, wantErr error, p []byte) {
t.Helper()
- if _, err := n.decode(p); !reflect.DeepEqual(err, wantErr) {
+ if _, err := n.decode(p); !errors.Is(err, wantErr) {
t.Fatal(fmt.Errorf("(%s) got err %q, want %q", n.ln.ID().TerminalString(), err, wantErr))
}
}
diff --git a/p2p/enode/localnode.go b/p2p/enode/localnode.go
index d8aa02a77e..4827b6c0ae 100644
--- a/p2p/enode/localnode.go
+++ b/p2p/enode/localnode.go
@@ -36,20 +36,25 @@ const (
iptrackMinStatements = 10
iptrackWindow = 5 * time.Minute
iptrackContactWindow = 10 * time.Minute
+
+ // time needed to wait between two updates to the local ENR
+ recordUpdateThrottle = time.Millisecond
)
// LocalNode produces the signed node record of a local node, i.e. a node run in the
// current process. Setting ENR entries via the Set method updates the record. A new version
// of the record is signed on demand when the Node method is called.
type LocalNode struct {
- cur atomic.Value // holds a non-nil node pointer while the record is up-to-date.
+ cur atomic.Value // holds a non-nil node pointer while the record is up-to-date
+
id ID
key *ecdsa.PrivateKey
db *DB
// everything below is protected by a lock
- mu sync.Mutex
+ mu sync.RWMutex
seq uint64
+ update time.Time // timestamp when the record was last updated
entries map[string]enr.Entry
endpoint4 lnEndpoint
endpoint6 lnEndpoint
@@ -76,7 +81,8 @@ func NewLocalNode(db *DB, key *ecdsa.PrivateKey) *LocalNode {
},
}
ln.seq = db.localSeq(ln.id)
- ln.invalidate()
+ ln.update = time.Now()
+ ln.cur.Store((*Node)(nil))
return ln
}
@@ -87,14 +93,34 @@ func (ln *LocalNode) Database() *DB {
// Node returns the current version of the local node record.
func (ln *LocalNode) Node() *Node {
+ // If we have a valid record, return that
n := ln.cur.Load().(*Node)
if n != nil {
return n
}
+
// Record was invalidated, sign a new copy.
ln.mu.Lock()
defer ln.mu.Unlock()
+
+ // Double check the current record, since multiple goroutines might be waiting
+ // on the write mutex.
+ if n = ln.cur.Load().(*Node); n != nil {
+ return n
+ }
+
+ // The initial sequence number is the current timestamp in milliseconds. To ensure
+ // that the initial sequence number will always be higher than any previous sequence
+ // number (assuming the clock is correct), we want to avoid updating the record faster
+ // than once per ms. So we need to sleep here until the next possible update time has
+ // arrived.
+ lastChange := time.Since(ln.update)
+ if lastChange < recordUpdateThrottle {
+ time.Sleep(recordUpdateThrottle - lastChange)
+ }
+
ln.sign()
+ ln.update = time.Now()
return ln.cur.Load().(*Node)
}
@@ -114,6 +140,10 @@ func (ln *LocalNode) ID() ID {
// Set puts the given entry into the local record, overwriting any existing value.
// Use Set*IP and SetFallbackUDP to set IP addresses and UDP port, otherwise they'll
// be overwritten by the endpoint predictor.
+//
+// Since node record updates are throttled to one per second, Set is asynchronous.
+// Any update will be queued up and published when at least one second passes from
+// the last change.
func (ln *LocalNode) Set(e enr.Entry) {
ln.mu.Lock()
defer ln.mu.Unlock()
@@ -288,3 +318,12 @@ func (ln *LocalNode) bumpSeq() {
ln.seq++
ln.db.storeLocalSeq(ln.id, ln.seq)
}
+
+// nowMilliseconds gives the current timestamp at millisecond precision.
+func nowMilliseconds() uint64 {
+ ns := time.Now().UnixNano()
+ if ns < 0 {
+ return 0
+ }
+ return uint64(ns / 1000 / 1000)
+}
diff --git a/p2p/enode/localnode_test.go b/p2p/enode/localnode_test.go
index 00746a8d27..312df813bb 100644
--- a/p2p/enode/localnode_test.go
+++ b/p2p/enode/localnode_test.go
@@ -49,32 +49,39 @@ func TestLocalNode(t *testing.T) {
}
}
+// This test checks that the sequence number is persisted between restarts.
func TestLocalNodeSeqPersist(t *testing.T) {
+ timestamp := nowMilliseconds()
+
ln, db := newLocalNodeForTesting()
defer db.Close()
- if s := ln.Node().Seq(); s != 1 {
- t.Fatalf("wrong initial seq %d, want 1", s)
+ initialSeq := ln.Node().Seq()
+ if initialSeq < timestamp {
+ t.Fatalf("wrong initial seq %d, want at least %d", initialSeq, timestamp)
}
+
ln.Set(enr.WithEntry("x", uint(1)))
- if s := ln.Node().Seq(); s != 2 {
- t.Fatalf("wrong seq %d after set, want 2", s)
+ if s := ln.Node().Seq(); s != initialSeq+1 {
+ t.Fatalf("wrong seq %d after set, want %d", s, initialSeq+1)
}
// Create a new instance, it should reload the sequence number.
// The number increases just after that because a new record is
// created without the "x" entry.
ln2 := NewLocalNode(db, ln.key)
- if s := ln2.Node().Seq(); s != 3 {
- t.Fatalf("wrong seq %d on new instance, want 3", s)
+ if s := ln2.Node().Seq(); s != initialSeq+2 {
+ t.Fatalf("wrong seq %d on new instance, want %d", s, initialSeq+2)
}
+ finalSeq := ln2.Node().Seq()
+
// Create a new instance with a different node key on the same database.
// This should reset the sequence number.
key, _ := crypto.GenerateKey()
ln3 := NewLocalNode(db, key)
- if s := ln3.Node().Seq(); s != 1 {
- t.Fatalf("wrong seq %d on instance with changed key, want 1", s)
+ if s := ln3.Node().Seq(); s < finalSeq {
+ t.Fatalf("wrong seq %d on instance with changed key, want >= %d", s, finalSeq)
}
}
@@ -91,20 +98,20 @@ func TestLocalNodeEndpoint(t *testing.T) {
// Nothing is set initially.
assert.Equal(t, net.IP(nil), ln.Node().IP())
assert.Equal(t, 0, ln.Node().UDP())
- assert.Equal(t, uint64(1), ln.Node().Seq())
+ initialSeq := ln.Node().Seq()
// Set up fallback address.
ln.SetFallbackIP(fallback.IP)
ln.SetFallbackUDP(fallback.Port)
assert.Equal(t, fallback.IP, ln.Node().IP())
assert.Equal(t, fallback.Port, ln.Node().UDP())
- assert.Equal(t, uint64(2), ln.Node().Seq())
+ assert.Equal(t, initialSeq+1, ln.Node().Seq())
// Add endpoint statements from random hosts.
for i := 0; i < iptrackMinStatements; i++ {
assert.Equal(t, fallback.IP, ln.Node().IP())
assert.Equal(t, fallback.Port, ln.Node().UDP())
- assert.Equal(t, uint64(2), ln.Node().Seq())
+ assert.Equal(t, initialSeq+1, ln.Node().Seq())
from := &net.UDPAddr{IP: make(net.IP, 4), Port: 90}
rand.Read(from.IP)
@@ -112,11 +119,11 @@ func TestLocalNodeEndpoint(t *testing.T) {
}
assert.Equal(t, predicted.IP, ln.Node().IP())
assert.Equal(t, predicted.Port, ln.Node().UDP())
- assert.Equal(t, uint64(3), ln.Node().Seq())
+ assert.Equal(t, initialSeq+2, ln.Node().Seq())
// Static IP overrides prediction.
ln.SetStaticIP(staticIP)
assert.Equal(t, staticIP, ln.Node().IP())
assert.Equal(t, fallback.Port, ln.Node().UDP())
- assert.Equal(t, uint64(4), ln.Node().Seq())
+ assert.Equal(t, initialSeq+3, ln.Node().Seq())
}
diff --git a/p2p/enode/nodedb.go b/p2p/enode/nodedb.go
index d62f383f0b..d1712f7597 100644
--- a/p2p/enode/nodedb.go
+++ b/p2p/enode/nodedb.go
@@ -427,9 +427,14 @@ func (db *DB) UpdateFindFailsV5(id ID, ip net.IP, fails int) error {
return db.storeInt64(v5Key(id, ip, dbNodeFindFails), int64(fails))
}
-// LocalSeq retrieves the local record sequence counter.
+// localSeq retrieves the local record sequence counter, defaulting to the current
+// timestamp if no previous exists. This ensures that wiping all data associated
+// with a node (apart from its key) will not generate already used sequence nums.
func (db *DB) localSeq(id ID) uint64 {
- return db.fetchUint64(localItemKey(id, dbLocalSeq))
+ if seq := db.fetchUint64(localItemKey(id, dbLocalSeq)); seq > 0 {
+ return seq
+ }
+ return nowMilliseconds()
}
// storeLocalSeq stores the local record sequence counter.
diff --git a/p2p/netutil/toobig_notwindows.go b/p2p/netutil/toobig_notwindows.go
index 47b6438572..f9f936ae56 100644
--- a/p2p/netutil/toobig_notwindows.go
+++ b/p2p/netutil/toobig_notwindows.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 !windows
+//go:build !windows
+// +build !windows
package netutil
diff --git a/p2p/netutil/toobig_windows.go b/p2p/netutil/toobig_windows.go
index dfbb6d44f0..652903e83c 100644
--- a/p2p/netutil/toobig_windows.go
+++ b/p2p/netutil/toobig_windows.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 windows
+//go:build windows
+// +build windows
package netutil
diff --git a/p2p/server_test.go b/p2p/server_test.go
index a5b3190aed..f6f5700c5e 100644
--- a/p2p/server_test.go
+++ b/p2p/server_test.go
@@ -370,6 +370,8 @@ func TestServerSetupConn(t *testing.T) {
clientkey, srvkey = newkey(), newkey()
clientpub = &clientkey.PublicKey
srvpub = &srvkey.PublicKey
+ fooErr = errors.New("foo")
+ readErr = errors.New("read error")
)
tests := []struct {
dontstart bool
@@ -387,10 +389,10 @@ func TestServerSetupConn(t *testing.T) {
wantCloseErr: errServerStopped,
},
{
- tt: &setupTransport{pubkey: clientpub, encHandshakeErr: errors.New("read error")},
+ tt: &setupTransport{pubkey: clientpub, encHandshakeErr: readErr},
flags: inboundConn,
wantCalls: "doEncHandshake,close,",
- wantCloseErr: errors.New("read error"),
+ wantCloseErr: readErr,
},
{
tt: &setupTransport{pubkey: clientpub, phs: protoHandshake{ID: randomID().Bytes()}},
@@ -400,11 +402,11 @@ func TestServerSetupConn(t *testing.T) {
wantCloseErr: DiscUnexpectedIdentity,
},
{
- tt: &setupTransport{pubkey: clientpub, protoHandshakeErr: errors.New("foo")},
+ tt: &setupTransport{pubkey: clientpub, protoHandshakeErr: fooErr},
dialDest: enode.NewV4(clientpub, nil, 0, 0),
flags: dynDialedConn,
wantCalls: "doEncHandshake,doProtoHandshake,close,",
- wantCloseErr: errors.New("foo"),
+ wantCloseErr: fooErr,
},
{
tt: &setupTransport{pubkey: srvpub, phs: protoHandshake{ID: crypto.FromECDSAPub(srvpub)[1:]}},
@@ -443,7 +445,7 @@ func TestServerSetupConn(t *testing.T) {
}
p1, _ := net.Pipe()
srv.SetupConn(p1, test.flags, test.dialDest)
- if !reflect.DeepEqual(test.tt.closeErr, test.wantCloseErr) {
+ if !errors.Is(test.tt.closeErr, test.wantCloseErr) {
t.Errorf("test %d: close error mismatch: got %q, want %q", i, test.tt.closeErr, test.wantCloseErr)
}
if test.tt.calls != test.wantCalls {
diff --git a/p2p/simulations/network.go b/p2p/simulations/network.go
index 9b5e2c37f5..962910dd25 100644
--- a/p2p/simulations/network.go
+++ b/p2p/simulations/network.go
@@ -22,7 +22,6 @@ import (
"encoding/json"
"errors"
"fmt"
- "io"
"math/rand"
"sync"
"time"
@@ -695,12 +694,6 @@ func (net *Network) Shutdown() {
if err := node.Stop(); err != nil {
log.Warn("Can't stop node", "id", node.ID(), "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(net.quitc)
}
diff --git a/params/config.go b/params/config.go
index 16c8018257..4715fbacda 100644
--- a/params/config.go
+++ b/params/config.go
@@ -69,6 +69,7 @@ var (
MuirGlacierBlock: big.NewInt(9_200_000),
BerlinBlock: big.NewInt(12_244_000),
LondonBlock: big.NewInt(12_965_000),
+ ArrowGlacierBlock: big.NewInt(13_773_000),
Ethash: new(EthashConfig),
}
@@ -151,6 +152,7 @@ var (
MuirGlacierBlock: nil,
BerlinBlock: big.NewInt(8_290_928),
LondonBlock: big.NewInt(8_897_988),
+ ArrowGlacierBlock: nil,
Clique: &CliqueConfig{
Period: 15,
Epoch: 30000,
@@ -193,6 +195,7 @@ var (
MuirGlacierBlock: nil,
BerlinBlock: big.NewInt(4_460_644),
LondonBlock: big.NewInt(5_062_605),
+ ArrowGlacierBlock: nil,
Clique: &CliqueConfig{
Period: 15,
Epoch: 30000,
@@ -225,16 +228,16 @@ var (
//
// This configuration is intentionally not using keyed fields to force anyone
// adding flags to the config to also have to set these fields.
- AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil}
+ AllEthashProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil}
// AllCliqueProtocolChanges contains every protocol change (EIPs) introduced
// and accepted by the Ethereum core developers into the Clique consensus.
//
// This configuration is intentionally not using keyed fields to force anyone
// adding flags to the config to also have to set these fields.
- AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}}
+ AllCliqueProtocolChanges = &ChainConfig{big.NewInt(1337), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, nil, nil, &CliqueConfig{Period: 0, Epoch: 30000}}
- TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil}
+ TestChainConfig = &ChainConfig{big.NewInt(1), big.NewInt(0), nil, false, big.NewInt(0), common.Hash{}, big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), big.NewInt(0), nil, new(EthashConfig), nil}
TestRules = TestChainConfig.Rules(new(big.Int))
)
@@ -313,8 +316,11 @@ type ChainConfig struct {
MuirGlacierBlock *big.Int `json:"muirGlacierBlock,omitempty"` // Eip-2384 (bomb delay) switch block (nil = no fork, 0 = already activated)
BerlinBlock *big.Int `json:"berlinBlock,omitempty"` // Berlin switch block (nil = no fork, 0 = already on berlin)
LondonBlock *big.Int `json:"londonBlock,omitempty"` // London switch block (nil = no fork, 0 = already on london)
+ ArrowGlacierBlock *big.Int `json:"arrowGlacierBlock,omitempty"` // Eip-4345 (bomb delay) switch block (nil = no fork, 0 = already activated)
- CatalystBlock *big.Int `json:"catalystBlock,omitempty"` // Catalyst switch block (nil = no fork, 0 = already on catalyst)
+ // TerminalTotalDifficulty is the amount of total difficulty reached by
+ // the network that triggers the consensus upgrade.
+ TerminalTotalDifficulty *big.Int `json:"terminalTotalDifficulty,omitempty"`
// Various consensus engines
Ethash *EthashConfig `json:"ethash,omitempty"`
@@ -351,7 +357,7 @@ func (c *ChainConfig) String() string {
default:
engine = "unknown"
}
- return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Engine: %v}",
+ return fmt.Sprintf("{ChainID: %v Homestead: %v DAO: %v DAOSupport: %v EIP150: %v EIP155: %v EIP158: %v Byzantium: %v Constantinople: %v Petersburg: %v Istanbul: %v, Muir Glacier: %v, Berlin: %v, London: %v, Arrow Glacier: %v, Engine: %v}",
c.ChainID,
c.HomesteadBlock,
c.DAOForkBlock,
@@ -366,6 +372,7 @@ func (c *ChainConfig) String() string {
c.MuirGlacierBlock,
c.BerlinBlock,
c.LondonBlock,
+ c.ArrowGlacierBlock,
engine,
)
}
@@ -432,9 +439,17 @@ func (c *ChainConfig) IsLondon(num *big.Int) bool {
return isForked(c.LondonBlock, num)
}
-// IsCatalyst returns whether num is either equal to the Merge fork block or greater.
-func (c *ChainConfig) IsCatalyst(num *big.Int) bool {
- return isForked(c.CatalystBlock, num)
+// IsArrowGlacier returns whether num is either equal to the Arrow Glacier (EIP-4345) fork block or greater.
+func (c *ChainConfig) IsArrowGlacier(num *big.Int) bool {
+ return isForked(c.ArrowGlacierBlock, num)
+}
+
+// IsTerminalPoWBlock returns whether the given block is the last block of PoW stage.
+func (c *ChainConfig) IsTerminalPoWBlock(parentTotalDiff *big.Int, totalDiff *big.Int) bool {
+ if c.TerminalTotalDifficulty == nil {
+ return false
+ }
+ return parentTotalDiff.Cmp(c.TerminalTotalDifficulty) < 0 && totalDiff.Cmp(c.TerminalTotalDifficulty) >= 0
}
// CheckCompatible checks whether scheduled fork transitions have been imported
@@ -477,6 +492,7 @@ func (c *ChainConfig) CheckConfigForkOrder() error {
{name: "muirGlacierBlock", block: c.MuirGlacierBlock, optional: true},
{name: "berlinBlock", block: c.BerlinBlock},
{name: "londonBlock", block: c.LondonBlock},
+ {name: "arrowGlacierBlock", block: c.ArrowGlacierBlock, optional: true},
} {
if lastFork.name != "" {
// Next one must be higher number
@@ -546,6 +562,9 @@ func (c *ChainConfig) checkCompatible(newcfg *ChainConfig, head *big.Int) *Confi
if isForkIncompatible(c.LondonBlock, newcfg.LondonBlock, head) {
return newCompatError("London fork block", c.LondonBlock, newcfg.LondonBlock)
}
+ if isForkIncompatible(c.ArrowGlacierBlock, newcfg.ArrowGlacierBlock, head) {
+ return newCompatError("Arrow Glacier fork block", c.ArrowGlacierBlock, newcfg.ArrowGlacierBlock)
+ }
return nil
}
@@ -613,7 +632,7 @@ type Rules struct {
ChainID *big.Int
IsHomestead, IsEIP150, IsEIP155, IsEIP158 bool
IsByzantium, IsConstantinople, IsPetersburg, IsIstanbul bool
- IsBerlin, IsLondon, IsCatalyst bool
+ IsBerlin, IsLondon bool
}
// Rules ensures c's ChainID is not nil.
@@ -634,6 +653,5 @@ func (c *ChainConfig) Rules(num *big.Int) Rules {
IsIstanbul: c.IsIstanbul(num),
IsBerlin: c.IsBerlin(num),
IsLondon: c.IsLondon(num),
- IsCatalyst: c.IsCatalyst(num),
}
}
diff --git a/params/version.go b/params/version.go
index dd629a100a..9d4ce56dc6 100644
--- a/params/version.go
+++ b/params/version.go
@@ -23,7 +23,7 @@ import (
const (
VersionMajor = 1 // Major version component of the current release
VersionMinor = 10 // Minor version component of the current release
- VersionPatch = 9 // Patch version component of the current release
+ VersionPatch = 12 // Patch version component of the current release
VersionMeta = "unstable" // Version metadata to append to the version string
)
diff --git a/rlp/decode.go b/rlp/decode.go
index ac04d5d569..5f2e5ad5fe 100644
--- a/rlp/decode.go
+++ b/rlp/decode.go
@@ -379,7 +379,7 @@ func decodeByteArray(s *Stream, val reflect.Value) error {
if err != nil {
return err
}
- slice := byteArrayBytes(val)
+ slice := byteArrayBytes(val, val.Len())
switch kind {
case Byte:
if len(slice) == 0 {
diff --git a/rlp/encode.go b/rlp/encode.go
index 3348644342..1623e97a3e 100644
--- a/rlp/encode.go
+++ b/rlp/encode.go
@@ -432,7 +432,20 @@ func makeByteArrayWriter(typ reflect.Type) writer {
case 1:
return writeLengthOneByteArray
default:
- return writeByteArray
+ length := typ.Len()
+ return func(val reflect.Value, w *encbuf) error {
+ if !val.CanAddr() {
+ // Getting the byte slice of val requires it to be addressable. Make it
+ // addressable by copying.
+ copy := reflect.New(val.Type()).Elem()
+ copy.Set(val)
+ val = copy
+ }
+ slice := byteArrayBytes(val, length)
+ w.encodeStringHeader(len(slice))
+ w.str = append(w.str, slice...)
+ return nil
+ }
}
}
@@ -451,21 +464,6 @@ func writeLengthOneByteArray(val reflect.Value, w *encbuf) error {
return nil
}
-func writeByteArray(val reflect.Value, w *encbuf) error {
- if !val.CanAddr() {
- // Getting the byte slice of val requires it to be addressable. Make it
- // addressable by copying.
- copy := reflect.New(val.Type()).Elem()
- copy.Set(val)
- val = copy
- }
-
- slice := byteArrayBytes(val)
- w.encodeStringHeader(len(slice))
- w.str = append(w.str, slice...)
- return nil
-}
-
func writeString(val reflect.Value, w *encbuf) error {
s := val.String()
if len(s) == 1 && s[0] <= 0x7f {
@@ -499,19 +497,39 @@ func makeSliceWriter(typ reflect.Type, ts tags) (writer, error) {
if etypeinfo.writerErr != nil {
return nil, etypeinfo.writerErr
}
- writer := func(val reflect.Value, w *encbuf) error {
- if !ts.tail {
- defer w.listEnd(w.list())
- }
- vlen := val.Len()
- for i := 0; i < vlen; i++ {
- if err := etypeinfo.writer(val.Index(i), w); err != nil {
- return err
+
+ var wfn writer
+ if ts.tail {
+ // This is for struct tail slices.
+ // w.list is not called for them.
+ wfn = func(val reflect.Value, w *encbuf) error {
+ vlen := val.Len()
+ for i := 0; i < vlen; i++ {
+ if err := etypeinfo.writer(val.Index(i), w); err != nil {
+ return err
+ }
}
+ return nil
+ }
+ } else {
+ // This is for regular slices and arrays.
+ wfn = func(val reflect.Value, w *encbuf) error {
+ vlen := val.Len()
+ if vlen == 0 {
+ w.str = append(w.str, 0xC0)
+ return nil
+ }
+ listOffset := w.list()
+ for i := 0; i < vlen; i++ {
+ if err := etypeinfo.writer(val.Index(i), w); err != nil {
+ return err
+ }
+ }
+ w.listEnd(listOffset)
+ return nil
}
- return nil
}
- return writer, nil
+ return wfn, nil
}
func makeStructWriter(typ reflect.Type) (writer, error) {
@@ -562,12 +580,8 @@ func makeStructWriter(typ reflect.Type) (writer, error) {
return writer, nil
}
-func makePtrWriter(typ reflect.Type, ts tags) (writer, error) {
- etypeinfo := theTC.infoWhileGenerating(typ.Elem(), tags{})
- if etypeinfo.writerErr != nil {
- return nil, etypeinfo.writerErr
- }
- // Determine how to encode nil pointers.
+// nilEncoding returns the encoded value of a nil pointer.
+func nilEncoding(typ reflect.Type, ts tags) uint8 {
var nilKind Kind
if ts.nilOK {
nilKind = ts.nilKind // use struct tag if provided
@@ -575,16 +589,29 @@ func makePtrWriter(typ reflect.Type, ts tags) (writer, error) {
nilKind = defaultNilKind(typ.Elem())
}
+ switch nilKind {
+ case String:
+ return 0x80
+ case List:
+ return 0xC0
+ default:
+ panic(fmt.Errorf("rlp: invalid nil kind %d", nilKind))
+ }
+}
+
+func makePtrWriter(typ reflect.Type, ts tags) (writer, error) {
+ etypeinfo := theTC.infoWhileGenerating(typ.Elem(), tags{})
+ if etypeinfo.writerErr != nil {
+ return nil, etypeinfo.writerErr
+ }
+ nilEncoding := nilEncoding(typ, ts)
+
writer := func(val reflect.Value, w *encbuf) error {
- if val.IsNil() {
- if nilKind == String {
- w.str = append(w.str, 0x80)
- } else {
- w.listEnd(w.list())
- }
- return nil
+ if ev := val.Elem(); ev.IsValid() {
+ return etypeinfo.writer(ev, w)
}
- return etypeinfo.writer(val.Elem(), w)
+ w.str = append(w.str, nilEncoding)
+ return nil
}
return writer, nil
}
diff --git a/rlp/encode_test.go b/rlp/encode_test.go
index 25d4aac267..a63743440d 100644
--- a/rlp/encode_test.go
+++ b/rlp/encode_test.go
@@ -540,3 +540,31 @@ func BenchmarkEncodeByteArrayStruct(b *testing.B) {
}
}
}
+
+type structSliceElem struct {
+ X uint64
+ Y uint64
+ Z uint64
+}
+
+type structPtrSlice []*structSliceElem
+
+func BenchmarkEncodeStructPtrSlice(b *testing.B) {
+ var out bytes.Buffer
+ var value = structPtrSlice{
+ &structSliceElem{1, 1, 1},
+ &structSliceElem{2, 2, 2},
+ &structSliceElem{3, 3, 3},
+ &structSliceElem{5, 5, 5},
+ &structSliceElem{6, 6, 6},
+ &structSliceElem{7, 7, 7},
+ }
+
+ b.ReportAllocs()
+ for i := 0; i < b.N; i++ {
+ out.Reset()
+ if err := Encode(&out, &value); err != nil {
+ b.Fatal(err)
+ }
+ }
+}
diff --git a/rlp/raw_test.go b/rlp/raw_test.go
index 185e269d07..46adff22c5 100644
--- a/rlp/raw_test.go
+++ b/rlp/raw_test.go
@@ -18,8 +18,8 @@ package rlp
import (
"bytes"
+ "errors"
"io"
- "reflect"
"testing"
"testing/quick"
)
@@ -54,7 +54,7 @@ func TestCountValues(t *testing.T) {
if count != test.count {
t.Errorf("test %d: count mismatch, got %d want %d\ninput: %s", i, count, test.count, test.input)
}
- if !reflect.DeepEqual(err, test.err) {
+ if !errors.Is(err, test.err) {
t.Errorf("test %d: err mismatch, got %q want %q\ninput: %s", i, err, test.err, test.input)
}
}
diff --git a/rlp/safe.go b/rlp/safe.go
index a80380aef4..3c910337b6 100644
--- a/rlp/safe.go
+++ b/rlp/safe.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 nacl || js || !cgo
// +build nacl js !cgo
package rlp
@@ -21,6 +22,6 @@ package rlp
import "reflect"
// byteArrayBytes returns a slice of the byte array v.
-func byteArrayBytes(v reflect.Value) []byte {
- return v.Slice(0, v.Len()).Bytes()
+func byteArrayBytes(v reflect.Value, length int) []byte {
+ return v.Slice(0, length).Bytes()
}
diff --git a/rlp/unsafe.go b/rlp/unsafe.go
index 94ed5405a8..2152ba35fc 100644
--- a/rlp/unsafe.go
+++ b/rlp/unsafe.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 !nacl && !js && cgo
// +build !nacl,!js,cgo
package rlp
@@ -24,12 +25,11 @@ import (
)
// byteArrayBytes returns a slice of the byte array v.
-func byteArrayBytes(v reflect.Value) []byte {
- len := v.Len()
+func byteArrayBytes(v reflect.Value, length int) []byte {
var s []byte
hdr := (*reflect.SliceHeader)(unsafe.Pointer(&s))
hdr.Data = v.UnsafeAddr()
- hdr.Cap = len
- hdr.Len = len
+ hdr.Cap = length
+ hdr.Len = length
return s
}
diff --git a/rpc/client.go b/rpc/client.go
index 198ce63573..edd9ecf1b6 100644
--- a/rpc/client.go
+++ b/rpc/client.go
@@ -59,6 +59,12 @@ const (
maxClientSubscriptionBuffer = 20000
)
+const (
+ httpScheme = "http"
+ wsScheme = "ws"
+ ipcScheme = "ipc"
+)
+
// BatchElem is an element in a batch request.
type BatchElem struct {
Method string
@@ -75,7 +81,7 @@ type BatchElem struct {
// Client represents a connection to an RPC server.
type Client struct {
idgen func() ID // for subscriptions
- isHTTP bool
+ scheme string // connection type: http, ws or ipc
services *serviceRegistry
idCounter uint32
@@ -111,6 +117,10 @@ type clientConn struct {
func (c *Client) newClientConn(conn ServerCodec) *clientConn {
ctx := context.WithValue(context.Background(), clientContextKey{}, c)
+ // Http connections have already set the scheme
+ if !c.isHTTP() && c.scheme != "" {
+ ctx = context.WithValue(ctx, "scheme", c.scheme)
+ }
handler := newHandler(ctx, conn, c.idgen, c.services)
return &clientConn{conn, handler}
}
@@ -136,7 +146,7 @@ func (op *requestOp) wait(ctx context.Context, c *Client) (*jsonrpcMessage, erro
select {
case <-ctx.Done():
// Send the timeout to dispatch so it can remove the request IDs.
- if !c.isHTTP {
+ if !c.isHTTP() {
select {
case c.reqTimeout <- op:
case <-c.closing:
@@ -203,10 +213,18 @@ func newClient(initctx context.Context, connect reconnectFunc) (*Client, error)
}
func initClient(conn ServerCodec, idgen func() ID, services *serviceRegistry) *Client {
- _, isHTTP := conn.(*httpConn)
+ scheme := ""
+ switch conn.(type) {
+ case *httpConn:
+ scheme = httpScheme
+ case *websocketCodec:
+ scheme = wsScheme
+ case *jsonCodec:
+ scheme = ipcScheme
+ }
c := &Client{
idgen: idgen,
- isHTTP: isHTTP,
+ scheme: scheme,
services: services,
writeConn: conn,
close: make(chan struct{}),
@@ -219,7 +237,7 @@ func initClient(conn ServerCodec, idgen func() ID, services *serviceRegistry) *C
reqSent: make(chan error, 1),
reqTimeout: make(chan *requestOp),
}
- if !isHTTP {
+ if !c.isHTTP() {
go c.dispatch(conn)
}
return c
@@ -250,7 +268,7 @@ func (c *Client) SupportedModules() (map[string]string, error) {
// Close closes the client, aborting any in-flight requests.
func (c *Client) Close() {
- if c.isHTTP {
+ if c.isHTTP() {
return
}
select {
@@ -264,7 +282,7 @@ func (c *Client) Close() {
// This method only works for clients using HTTP, it doesn't have
// any effect for clients using another transport.
func (c *Client) SetHeader(key, value string) {
- if !c.isHTTP {
+ if !c.isHTTP() {
return
}
conn := c.writeConn.(*httpConn)
@@ -298,7 +316,7 @@ func (c *Client) CallContext(ctx context.Context, result interface{}, method str
}
op := &requestOp{ids: []json.RawMessage{msg.ID}, resp: make(chan *jsonrpcMessage, 1)}
- if c.isHTTP {
+ if c.isHTTP() {
err = c.sendHTTP(ctx, op, msg)
} else {
err = c.send(ctx, op, msg)
@@ -357,7 +375,7 @@ func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error {
}
var err error
- if c.isHTTP {
+ if c.isHTTP() {
err = c.sendBatchHTTP(ctx, op, msgs)
} else {
err = c.send(ctx, op, msgs)
@@ -402,18 +420,18 @@ func (c *Client) Notify(ctx context.Context, method string, args ...interface{})
}
msg.ID = nil
- if c.isHTTP {
+ if c.isHTTP() {
return c.sendHTTP(ctx, op, msg)
}
return c.send(ctx, op, msg)
}
-// EthSubscribe registers a subscripion under the "eth" namespace.
+// EthSubscribe registers a subscription under the "eth" namespace.
func (c *Client) EthSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) {
return c.Subscribe(ctx, "eth", channel, args...)
}
-// ShhSubscribe registers a subscripion under the "shh" namespace.
+// ShhSubscribe registers a subscription under the "shh" namespace.
// Deprecated: use Subscribe(ctx, "shh", ...).
func (c *Client) ShhSubscribe(ctx context.Context, channel interface{}, args ...interface{}) (*ClientSubscription, error) {
return c.Subscribe(ctx, "shh", channel, args...)
@@ -440,7 +458,7 @@ func (c *Client) Subscribe(ctx context.Context, namespace string, channel interf
if chanVal.IsNil() {
panic("channel given to Subscribe must not be nil")
}
- if c.isHTTP {
+ if c.isHTTP() {
return nil, ErrNotificationsUnsupported
}
@@ -642,3 +660,7 @@ func (c *Client) read(codec ServerCodec) {
c.readOp <- readOp{msgs, batch}
}
}
+
+func (c *Client) isHTTP() bool {
+ return c.scheme == httpScheme
+}
diff --git a/rpc/constants_unix.go b/rpc/constants_unix.go
index 2f98d6499b..1f04d15d7f 100644
--- a/rpc/constants_unix.go
+++ b/rpc/constants_unix.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 darwin || dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
package rpc
diff --git a/rpc/constants_unix_nocgo.go b/rpc/constants_unix_nocgo.go
index ecb231f927..a62e4ee529 100644
--- a/rpc/constants_unix_nocgo.go
+++ b/rpc/constants_unix_nocgo.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 !cgo && !windows
// +build !cgo,!windows
package rpc
diff --git a/rpc/ipc_js.go b/rpc/ipc_js.go
index 7e7554a768..453a20bc1a 100644
--- a/rpc/ipc_js.go
+++ b/rpc/ipc_js.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 js
// +build js
package rpc
diff --git a/rpc/ipc_unix.go b/rpc/ipc_unix.go
index f4690cc0ab..249a9cf044 100644
--- a/rpc/ipc_unix.go
+++ b/rpc/ipc_unix.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 darwin || dragonfly || freebsd || linux || nacl || netbsd || openbsd || solaris
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris
package rpc
diff --git a/rpc/ipc_windows.go b/rpc/ipc_windows.go
index ca56a3ce43..adb1826f0c 100644
--- a/rpc/ipc_windows.go
+++ b/rpc/ipc_windows.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 windows
// +build windows
package rpc
diff --git a/rpc/types.go b/rpc/types.go
index ad068defa8..ca52d474d9 100644
--- a/rpc/types.go
+++ b/rpc/types.go
@@ -98,6 +98,22 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
return nil
}
+// MarshalText implements encoding.TextMarshaler. It marshals:
+// - "latest", "earliest" or "pending" as strings
+// - other numbers as hex
+func (bn BlockNumber) MarshalText() ([]byte, error) {
+ switch bn {
+ case EarliestBlockNumber:
+ return []byte("earliest"), nil
+ case LatestBlockNumber:
+ return []byte("latest"), nil
+ case PendingBlockNumber:
+ return []byte("pending"), nil
+ default:
+ return hexutil.Uint64(bn).MarshalText()
+ }
+}
+
func (bn BlockNumber) Int64() int64 {
return (int64)(bn)
}
@@ -170,6 +186,16 @@ func (bnh *BlockNumberOrHash) Number() (BlockNumber, bool) {
return BlockNumber(0), false
}
+func (bnh *BlockNumberOrHash) String() string {
+ if bnh.BlockNumber != nil {
+ return strconv.Itoa(int(*bnh.BlockNumber))
+ }
+ if bnh.BlockHash != nil {
+ return bnh.BlockHash.String()
+ }
+ return "nil"
+}
+
func (bnh *BlockNumberOrHash) Hash() (common.Hash, bool) {
if bnh.BlockHash != nil {
return *bnh.BlockHash, true
diff --git a/rpc/types_test.go b/rpc/types_test.go
index 89b0c9171a..f110dee7c6 100644
--- a/rpc/types_test.go
+++ b/rpc/types_test.go
@@ -18,6 +18,7 @@ package rpc
import (
"encoding/json"
+ "reflect"
"testing"
"github.com/ethereum/go-ethereum/common"
@@ -122,3 +123,33 @@ func TestBlockNumberOrHash_UnmarshalJSON(t *testing.T) {
}
}
}
+
+func TestBlockNumberOrHash_WithNumber_MarshalAndUnmarshal(t *testing.T) {
+ tests := []struct {
+ name string
+ number int64
+ }{
+ {"max", math.MaxInt64},
+ {"pending", int64(PendingBlockNumber)},
+ {"latest", int64(LatestBlockNumber)},
+ {"earliest", int64(EarliestBlockNumber)},
+ }
+ for _, test := range tests {
+ test := test
+ t.Run(test.name, func(t *testing.T) {
+ bnh := BlockNumberOrHashWithNumber(BlockNumber(test.number))
+ marshalled, err := json.Marshal(bnh)
+ if err != nil {
+ t.Fatal("cannot marshal:", err)
+ }
+ var unmarshalled BlockNumberOrHash
+ err = json.Unmarshal(marshalled, &unmarshalled)
+ if err != nil {
+ t.Fatal("cannot unmarshal:", err)
+ }
+ if !reflect.DeepEqual(bnh, unmarshalled) {
+ t.Fatalf("wrong result: expected %v, got %v", bnh, unmarshalled)
+ }
+ })
+ }
+}
diff --git a/rpc/websocket.go b/rpc/websocket.go
index afeb4c2081..5571324af8 100644
--- a/rpc/websocket.go
+++ b/rpc/websocket.go
@@ -37,6 +37,7 @@ const (
wsWriteBuffer = 1024
wsPingInterval = 60 * time.Second
wsPingWriteTimeout = 5 * time.Second
+ wsPongTimeout = 30 * time.Second
wsMessageSizeLimit = 15 * 1024 * 1024
)
@@ -241,6 +242,10 @@ type websocketCodec struct {
func newWebsocketCodec(conn *websocket.Conn) ServerCodec {
conn.SetReadLimit(wsMessageSizeLimit)
+ conn.SetPongHandler(func(appData string) error {
+ conn.SetReadDeadline(time.Time{})
+ return nil
+ })
wc := &websocketCodec{
jsonCodec: NewFuncCodec(conn, conn.WriteJSON, conn.ReadJSON).(*jsonCodec),
conn: conn,
@@ -287,6 +292,7 @@ func (wc *websocketCodec) pingLoop() {
wc.jsonCodec.encMu.Lock()
wc.conn.SetWriteDeadline(time.Now().Add(wsPingWriteTimeout))
wc.conn.WriteMessage(websocket.PingMessage, nil)
+ wc.conn.SetReadDeadline(time.Now().Add(wsPongTimeout))
wc.jsonCodec.encMu.Unlock()
timer.Reset(wsPingInterval)
}
diff --git a/rpc/websocket_test.go b/rpc/websocket_test.go
index 4976853baf..cf83b621f1 100644
--- a/rpc/websocket_test.go
+++ b/rpc/websocket_test.go
@@ -18,11 +18,15 @@ package rpc
import (
"context"
+ "errors"
+ "io"
"net"
"net/http"
"net/http/httptest"
- "reflect"
+ "net/http/httputil"
+ "net/url"
"strings"
+ "sync/atomic"
"testing"
"time"
@@ -65,7 +69,7 @@ func TestWebsocketOriginCheck(t *testing.T) {
t.Fatal("no error for wrong origin")
}
wantErr := wsHandshakeError{websocket.ErrBadHandshake, "403 Forbidden"}
- if !reflect.DeepEqual(err, wantErr) {
+ if !errors.Is(err, wantErr) {
t.Fatalf("wrong error for wrong origin: %q", err)
}
@@ -188,6 +192,63 @@ func TestClientWebsocketLargeMessage(t *testing.T) {
}
}
+func TestClientWebsocketSevered(t *testing.T) {
+ t.Parallel()
+
+ var (
+ server = wsPingTestServer(t, nil)
+ ctx = context.Background()
+ )
+ defer server.Shutdown(ctx)
+
+ u, err := url.Parse("http://" + server.Addr)
+ if err != nil {
+ t.Fatal(err)
+ }
+ rproxy := httputil.NewSingleHostReverseProxy(u)
+ var severable *severableReadWriteCloser
+ rproxy.ModifyResponse = func(response *http.Response) error {
+ severable = &severableReadWriteCloser{ReadWriteCloser: response.Body.(io.ReadWriteCloser)}
+ response.Body = severable
+ return nil
+ }
+ frontendProxy := httptest.NewServer(rproxy)
+ defer frontendProxy.Close()
+
+ wsURL := "ws:" + strings.TrimPrefix(frontendProxy.URL, "http:")
+ client, err := DialWebsocket(ctx, wsURL, "")
+ if err != nil {
+ t.Fatalf("client dial error: %v", err)
+ }
+ defer client.Close()
+
+ resultChan := make(chan int)
+ sub, err := client.EthSubscribe(ctx, resultChan, "foo")
+ if err != nil {
+ t.Fatalf("client subscribe error: %v", err)
+ }
+
+ // sever the connection
+ severable.Sever()
+
+ // Wait for subscription error.
+ timeout := time.NewTimer(3 * wsPingInterval)
+ defer timeout.Stop()
+ for {
+ select {
+ case err := <-sub.Err():
+ t.Log("client subscription error:", err)
+ return
+ case result := <-resultChan:
+ t.Error("unexpected result:", result)
+ return
+ case <-timeout.C:
+ t.Error("didn't get any error within the test timeout")
+ return
+ }
+ }
+}
+
// wsPingTestServer runs a WebSocket server which accepts a single subscription request.
// When a value arrives on sendPing, the server sends a ping frame, waits for a matching
// pong and finally delivers a single subscription result.
@@ -290,3 +351,31 @@ func wsPingTestHandler(t *testing.T, conn *websocket.Conn, shutdown, sendPing <-
}
}
}
+
+// severableReadWriteCloser wraps an io.ReadWriteCloser and provides a Sever() method to drop writes and read empty.
+type severableReadWriteCloser struct {
+ io.ReadWriteCloser
+ severed int32 // atomic
+}
+
+func (s *severableReadWriteCloser) Sever() {
+ atomic.StoreInt32(&s.severed, 1)
+}
+
+func (s *severableReadWriteCloser) Read(p []byte) (n int, err error) {
+ if atomic.LoadInt32(&s.severed) > 0 {
+ return 0, nil
+ }
+ return s.ReadWriteCloser.Read(p)
+}
+
+func (s *severableReadWriteCloser) Write(p []byte) (n int, err error) {
+ if atomic.LoadInt32(&s.severed) > 0 {
+ return len(p), nil
+ }
+ return s.ReadWriteCloser.Write(p)
+}
+
+func (s *severableReadWriteCloser) Close() error {
+ return s.ReadWriteCloser.Close()
+}
diff --git a/signer/core/signed_data.go b/signer/core/signed_data.go
index 6c2fd5f8fe..daa84313da 100644
--- a/signer/core/signed_data.go
+++ b/signer/core/signed_data.go
@@ -29,6 +29,7 @@ import (
"strconv"
"strings"
"unicode"
+ "unicode/utf8"
"github.com/ethereum/go-ethereum/accounts"
"github.com/ethereum/go-ethereum/common"
@@ -100,7 +101,8 @@ func (t *Type) isReferenceType() bool {
return false
}
// Reference types must have a leading uppercase character
- return unicode.IsUpper([]rune(t.Type)[0])
+ r, _ := utf8.DecodeRuneInString(t.Type)
+ return unicode.IsUpper(r)
}
type Types map[string][]Type
diff --git a/signer/core/validation.go b/signer/core/validation.go
index af858862ef..7639dbf649 100644
--- a/signer/core/validation.go
+++ b/signer/core/validation.go
@@ -21,7 +21,7 @@ import (
"regexp"
)
-var printable7BitAscii = regexp.MustCompile("^[A-Za-z0-9!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~ ]+$")
+var printable7BitAscii = regexp.MustCompile("^[A-Za-z0-9!\"#$%&'()*+,\\-./:;<=>?@[\\]^_`{|}~ ]+$")
// ValidatePasswordFormat returns an error if the password is too short, or consists of characters
// outside the range of the printable 7bit ascii set
diff --git a/signer/rules/deps/bignumber.js b/signer/rules/deps/bignumber.js
deleted file mode 100644
index 17c8851e24..0000000000
--- a/signer/rules/deps/bignumber.js
+++ /dev/null
@@ -1,4 +0,0 @@
-/* bignumber.js v2.0.3 https://github.com/MikeMcl/bignumber.js/LICENCE */
-/* modified by zelig to fix https://github.com/robertkrimen/otto#regular-expression-incompatibility */
-!function(e){"use strict";function n(e){function a(e,n){var t,r,i,o,u,s,f=this;if(!(f instanceof a))return j&&L(26,"constructor call without new",e),new a(e,n);if(null!=n&&H(n,2,64,M,"base")){if(n=0|n,s=e+"",10==n)return f=new a(e instanceof a?e:s),U(f,P+f.e+1,k);if((o="number"==typeof e)&&0*e!=0||!new RegExp("^-?"+(t="["+O.slice(0,n)+"]+")+"(?:\\."+t+")?$",37>n?"i":"").test(s))return g(f,s,o,n);o?(f.s=0>1/e?(s=s.slice(1),-1):1,j&&s.replace(/^0\.0*|\./,"").length>15&&L(M,b,e),o=!1):f.s=45===s.charCodeAt(0)?(s=s.slice(1),-1):1,s=D(s,10,n,f.s)}else{if(e instanceof a)return f.s=e.s,f.e=e.e,f.c=(e=e.c)?e.slice():e,void(M=0);if((o="number"==typeof e)&&0*e==0){if(f.s=0>1/e?(e=-e,-1):1,e===~~e){for(r=0,i=e;i>=10;i/=10,r++);return f.e=r,f.c=[e],void(M=0)}s=e+""}else{if(!p.test(s=e+""))return g(f,s,o);f.s=45===s.charCodeAt(0)?(s=s.slice(1),-1):1}}for((r=s.indexOf("."))>-1&&(s=s.replace(".","")),(i=s.search(/e/i))>0?(0>r&&(r=i),r+=+s.slice(i+1),s=s.substring(0,i)):0>r&&(r=s.length),i=0;48===s.charCodeAt(i);i++);for(u=s.length;48===s.charCodeAt(--u););if(s=s.slice(i,u+1))if(u=s.length,o&&j&&u>15&&L(M,b,f.s*e),r=r-i-1,r>z)f.c=f.e=null;else if(G>r)f.c=[f.e=0];else{if(f.e=r,f.c=[],i=(r+1)%y,0>r&&(i+=y),u>i){for(i&&f.c.push(+s.slice(0,i)),u-=y;u>i;)f.c.push(+s.slice(i,i+=y));s=s.slice(i),i=y-s.length}else i-=u;for(;i--;s+="0");f.c.push(+s)}else f.c=[f.e=0];M=0}function D(e,n,t,i){var o,u,f,c,h,g,p,d=e.indexOf("."),m=P,w=k;for(37>t&&(e=e.toLowerCase()),d>=0&&(f=J,J=0,e=e.replace(".",""),p=new a(t),h=p.pow(e.length-d),J=f,p.c=s(l(r(h.c),h.e),10,n),p.e=p.c.length),g=s(e,t,n),u=f=g.length;0==g[--f];g.pop());if(!g[0])return"0";if(0>d?--u:(h.c=g,h.e=u,h.s=i,h=C(h,p,m,w,n),g=h.c,c=h.r,u=h.e),o=u+m+1,d=g[o],f=n/2,c=c||0>o||null!=g[o+1],c=4>w?(null!=d||c)&&(0==w||w==(h.s<0?3:2)):d>f||d==f&&(4==w||c||6==w&&1&g[o-1]||w==(h.s<0?8:7)),1>o||!g[0])e=c?l("1",-m):"0";else{if(g.length=o,c)for(--n;++g[--o]>n;)g[o]=0,o||(++u,g.unshift(1));for(f=g.length;!g[--f];);for(d=0,e="";f>=d;e+=O.charAt(g[d++]));e=l(e,u)}return e}function _(e,n,t,i){var o,u,s,c,h;if(t=null!=t&&H(t,0,8,i,v)?0|t:k,!e.c)return e.toString();if(o=e.c[0],s=e.e,null==n)h=r(e.c),h=19==i||24==i&&B>=s?f(h,s):l(h,s);else if(e=U(new a(e),n,t),u=e.e,h=r(e.c),c=h.length,19==i||24==i&&(u>=n||B>=u)){for(;n>c;h+="0",c++);h=f(h,u)}else if(n-=s,h=l(h,u),u+1>c){if(--n>0)for(h+=".";n--;h+="0");}else if(n+=u-c,n>0)for(u+1==c&&(h+=".");n--;h+="0");return e.s<0&&o?"-"+h:h}function x(e,n){var t,r,i=0;for(u(e[0])&&(e=e[0]),t=new a(e[0]);++ie||e>t||e!=c(e))&&L(r,(i||"decimal places")+(n>e||e>t?" out of range":" not an integer"),e),!0}function I(e,n,t){for(var r=1,i=n.length;!n[--i];n.pop());for(i=n[0];i>=10;i/=10,r++);return(t=r+t*y-1)>z?e.c=e.e=null:G>t?e.c=[e.e=0]:(e.e=t,e.c=n),e}function L(e,n,t){var r=new Error(["new BigNumber","cmp","config","div","divToInt","eq","gt","gte","lt","lte","minus","mod","plus","precision","random","round","shift","times","toDigits","toExponential","toFixed","toFormat","toFraction","pow","toPrecision","toString","BigNumber"][e]+"() "+n+": "+t);throw r.name="BigNumber Error",M=0,r}function U(e,n,t,r){var i,o,u,s,f,l,c,a=e.c,h=R;if(a){e:{for(i=1,s=a[0];s>=10;s/=10,i++);if(o=n-i,0>o)o+=y,u=n,f=a[l=0],c=f/h[i-u-1]%10|0;else if(l=d((o+1)/y),l>=a.length){if(!r)break e;for(;a.length<=l;a.push(0));f=c=0,i=1,o%=y,u=o-y+1}else{for(f=s=a[l],i=1;s>=10;s/=10,i++);o%=y,u=o-y+i,c=0>u?0:f/h[i-u-1]%10|0}if(r=r||0>n||null!=a[l+1]||(0>u?f:f%h[i-u-1]),r=4>t?(c||r)&&(0==t||t==(e.s<0?3:2)):c>5||5==c&&(4==t||r||6==t&&(o>0?u>0?f/h[i-u]:0:a[l-1])%10&1||t==(e.s<0?8:7)),1>n||!a[0])return a.length=0,r?(n-=e.e+1,a[0]=h[n%y],e.e=-n||0):a[0]=e.e=0,e;if(0==o?(a.length=l,s=1,l--):(a.length=l+1,s=h[y-o],a[l]=u>0?m(f/h[i-u]%h[u])*s:0),r)for(;;){if(0==l){for(o=1,u=a[0];u>=10;u/=10,o++);for(u=a[0]+=s,s=1;u>=10;u/=10,s++);o!=s&&(e.e++,a[0]==N&&(a[0]=1));break}if(a[l]+=s,a[l]!=N)break;a[l--]=0,s=1}for(o=a.length;0===a[--o];a.pop());}e.e>z?e.c=e.e=null:e.et?null!=(e=i[t++]):void 0};return f(n="DECIMAL_PLACES")&&H(e,0,E,2,n)&&(P=0|e),r[n]=P,f(n="ROUNDING_MODE")&&H(e,0,8,2,n)&&(k=0|e),r[n]=k,f(n="EXPONENTIAL_AT")&&(u(e)?H(e[0],-E,0,2,n)&&H(e[1],0,E,2,n)&&(B=0|e[0],$=0|e[1]):H(e,-E,E,2,n)&&(B=-($=0|(0>e?-e:e)))),r[n]=[B,$],f(n="RANGE")&&(u(e)?H(e[0],-E,-1,2,n)&&H(e[1],1,E,2,n)&&(G=0|e[0],z=0|e[1]):H(e,-E,E,2,n)&&(0|e?G=-(z=0|(0>e?-e:e)):j&&L(2,n+" cannot be zero",e))),r[n]=[G,z],f(n="ERRORS")&&(e===!!e||1===e||0===e?(M=0,H=(j=!!e)?F:o):j&&L(2,n+w,e)),r[n]=j,f(n="CRYPTO")&&(e===!!e||1===e||0===e?(V=!(!e||!h||"object"!=typeof h),e&&!V&&j&&L(2,"crypto unavailable",h)):j&&L(2,n+w,e)),r[n]=V,f(n="MODULO_MODE")&&H(e,0,9,2,n)&&(W=0|e),r[n]=W,f(n="POW_PRECISION")&&H(e,0,E,2,n)&&(J=0|e),r[n]=J,f(n="FORMAT")&&("object"==typeof e?X=e:j&&L(2,n+" not an object",e)),r[n]=X,r},a.max=function(){return x(arguments,T.lt)},a.min=function(){return x(arguments,T.gt)},a.random=function(){var e=9007199254740992,n=Math.random()*e&2097151?function(){return m(Math.random()*e)}:function(){return 8388608*(1073741824*Math.random()|0)+(8388608*Math.random()|0)};return function(e){var t,r,i,o,u,s=0,f=[],l=new a(q);if(e=null!=e&&H(e,0,E,14)?0|e:P,o=d(e/y),V)if(h&&h.getRandomValues){for(t=h.getRandomValues(new Uint32Array(o*=2));o>s;)u=131072*t[s]+(t[s+1]>>>11),u>=9e15?(r=h.getRandomValues(new Uint32Array(2)),t[s]=r[0],t[s+1]=r[1]):(f.push(u%1e14),s+=2);s=o/2}else if(h&&h.randomBytes){for(t=h.randomBytes(o*=7);o>s;)u=281474976710656*(31&t[s])+1099511627776*t[s+1]+4294967296*t[s+2]+16777216*t[s+3]+(t[s+4]<<16)+(t[s+5]<<8)+t[s+6],u>=9e15?h.randomBytes(7).copy(t,s):(f.push(u%1e14),s+=7);s=o/7}else j&&L(14,"crypto unavailable",h);if(!s)for(;o>s;)u=n(),9e15>u&&(f[s++]=u%1e14);for(o=f[--s],e%=y,o&&e&&(u=R[y-e],f[s]=m(o/u)*u);0===f[s];f.pop(),s--);if(0>s)f=[i=0];else{for(i=-1;0===f[0];f.shift(),i-=y);for(s=1,u=f[0];u>=10;u/=10,s++);y>s&&(i-=y-s)}return l.e=i,l.c=f,l}}(),C=function(){function e(e,n,t){var r,i,o,u,s=0,f=e.length,l=n%A,c=n/A|0;for(e=e.slice();f--;)o=e[f]%A,u=e[f]/A|0,r=c*o+u*l,i=l*o+r%A*A+s,s=(i/t|0)+(r/A|0)+c*u,e[f]=i%t;return s&&e.unshift(s),e}function n(e,n,t,r){var i,o;if(t!=r)o=t>r?1:-1;else for(i=o=0;t>i;i++)if(e[i]!=n[i]){o=e[i]>n[i]?1:-1;break}return o}function r(e,n,t,r){for(var i=0;t--;)e[t]-=i,i=e[t]1;e.shift());}return function(i,o,u,s,f){var l,c,h,g,p,d,w,v,b,O,S,R,A,E,D,_,x,F=i.s==o.s?1:-1,I=i.c,L=o.c;if(!(I&&I[0]&&L&&L[0]))return new a(i.s&&o.s&&(I?!L||I[0]!=L[0]:L)?I&&0==I[0]||!L?0*F:F/0:0/0);for(v=new a(F),b=v.c=[],c=i.e-o.e,F=u+c+1,f||(f=N,c=t(i.e/y)-t(o.e/y),F=F/y|0),h=0;L[h]==(I[h]||0);h++);if(L[h]>(I[h]||0)&&c--,0>F)b.push(1),g=!0;else{for(E=I.length,_=L.length,h=0,F+=2,p=m(f/(L[0]+1)),p>1&&(L=e(L,p,f),I=e(I,p,f),_=L.length,E=I.length),A=_,O=I.slice(0,_),S=O.length;_>S;O[S++]=0);x=L.slice(),x.unshift(0),D=L[0],L[1]>=f/2&&D++;do p=0,l=n(L,O,_,S),0>l?(R=O[0],_!=S&&(R=R*f+(O[1]||0)),p=m(R/D),p>1?(p>=f&&(p=f-1),d=e(L,p,f),w=d.length,S=O.length,l=n(d,O,w,S),1==l&&(p--,r(d,w>_?x:L,w,f))):(0==p&&(l=p=1),d=L.slice()),w=d.length,S>w&&d.unshift(0),r(O,d,S,f),-1==l&&(S=O.length,l=n(L,O,_,S),1>l&&(p++,r(O,S>_?x:L,S,f))),S=O.length):0===l&&(p++,O=[0]),b[h++]=p,l&&O[0]?O[S++]=I[A]||0:(O=[I[A]],S=1);while((A++=10;F/=10,h++);U(v,u+(v.e=h+c*y-1)+1,s,g)}else v.e=c,v.r=+g;return v}}(),g=function(){var e=/^(-?)0([xbo])(\w[\w.]*$)/i,n=/^([^.]+)\.$/,t=/^\.([^.]+)$/,r=/^-?(Infinity|NaN)$/,i=/^\s*\+([\w.])|^\s+|\s+$/g;return function(o,u,s,f){var l,c=s?u:u.replace(i,"$1");if(r.test(c))o.s=isNaN(c)?null:0>c?-1:1;else{if(!s&&(c=c.replace(e,function(e,n,t){return l="x"==(t=t.toLowerCase())?16:"b"==t?2:8,f&&f!=l?e:n}),f&&(l=f,c=c.replace(n,"$1").replace(t,"0.$1")),u!=c))return new a(c,l);j&&L(M,"not a"+(f?" base "+f:"")+" number",u),o.s=null}o.c=o.e=null,M=0}}(),T.absoluteValue=T.abs=function(){var e=new a(this);return e.s<0&&(e.s=1),e},T.ceil=function(){return U(new a(this),this.e+1,2)},T.comparedTo=T.cmp=function(e,n){return M=1,i(this,new a(e,n))},T.decimalPlaces=T.dp=function(){var e,n,r=this.c;if(!r)return null;if(e=((n=r.length-1)-t(this.e/y))*y,n=r[n])for(;n%10==0;n/=10,e--);return 0>e&&(e=0),e},T.dividedBy=T.div=function(e,n){return M=3,C(this,new a(e,n),P,k)},T.dividedToIntegerBy=T.divToInt=function(e,n){return M=4,C(this,new a(e,n),0,1)},T.equals=T.eq=function(e,n){return M=5,0===i(this,new a(e,n))},T.floor=function(){return U(new a(this),this.e+1,3)},T.greaterThan=T.gt=function(e,n){return M=6,i(this,new a(e,n))>0},T.greaterThanOrEqualTo=T.gte=function(e,n){return M=7,1===(n=i(this,new a(e,n)))||0===n},T.isFinite=function(){return!!this.c},T.isInteger=T.isInt=function(){return!!this.c&&t(this.e/y)>this.c.length-2},T.isNaN=function(){return!this.s},T.isNegative=T.isNeg=function(){return this.s<0},T.isZero=function(){return!!this.c&&0==this.c[0]},T.lessThan=T.lt=function(e,n){return M=8,i(this,new a(e,n))<0},T.lessThanOrEqualTo=T.lte=function(e,n){return M=9,-1===(n=i(this,new a(e,n)))||0===n},T.minus=T.sub=function(e,n){var r,i,o,u,s=this,f=s.s;if(M=10,e=new a(e,n),n=e.s,!f||!n)return new a(0/0);if(f!=n)return e.s=-n,s.plus(e);var l=s.e/y,c=e.e/y,h=s.c,g=e.c;if(!l||!c){if(!h||!g)return h?(e.s=-n,e):new a(g?s:0/0);if(!h[0]||!g[0])return g[0]?(e.s=-n,e):new a(h[0]?s:3==k?-0:0)}if(l=t(l),c=t(c),h=h.slice(),f=l-c){for((u=0>f)?(f=-f,o=h):(c=l,o=g),o.reverse(),n=f;n--;o.push(0));o.reverse()}else for(i=(u=(f=h.length)<(n=g.length))?f:n,f=n=0;i>n;n++)if(h[n]!=g[n]){u=h[n]0)for(;n--;h[r++]=0);for(n=N-1;i>f;){if(h[--i]0?(s=u,r=l):(o=-o,r=f),r.reverse();o--;r.push(0));r.reverse()}for(o=f.length,n=l.length,0>o-n&&(r=l,l=f,f=r,n=o),o=0;n;)o=(f[--n]=f[n]+l[n]+o)/N|0,f[n]%=N;return o&&(f.unshift(o),++s),I(e,f,s)},T.precision=T.sd=function(e){var n,t,r=this,i=r.c;if(null!=e&&e!==!!e&&1!==e&&0!==e&&(j&&L(13,"argument"+w,e),e!=!!e&&(e=null)),!i)return null;if(t=i.length-1,n=t*y+1,t=i[t]){for(;t%10==0;t/=10,n--);for(t=i[0];t>=10;t/=10,n++);}return e&&r.e+1>n&&(n=r.e+1),n},T.round=function(e,n){var t=new a(this);return(null==e||H(e,0,E,15))&&U(t,~~e+this.e+1,null!=n&&H(n,0,8,15,v)?0|n:k),t},T.shift=function(e){var n=this;return H(e,-S,S,16,"argument")?n.times("1e"+c(e)):new a(n.c&&n.c[0]&&(-S>e||e>S)?n.s*(0>e?0:1/0):n)},T.squareRoot=T.sqrt=function(){var e,n,i,o,u,s=this,f=s.c,l=s.s,c=s.e,h=P+4,g=new a("0.5");if(1!==l||!f||!f[0])return new a(!l||0>l&&(!f||f[0])?0/0:f?s:1/0);if(l=Math.sqrt(+s),0==l||l==1/0?(n=r(f),(n.length+c)%2==0&&(n+="0"),l=Math.sqrt(n),c=t((c+1)/2)-(0>c||c%2),l==1/0?n="1e"+c:(n=l.toExponential(),n=n.slice(0,n.indexOf("e")+1)+c),i=new a(n)):i=new a(l+""),i.c[0])for(c=i.e,l=c+h,3>l&&(l=0);;)if(u=i,i=g.times(u.plus(C(s,u,h,1))),r(u.c).slice(0,l)===(n=r(i.c)).slice(0,l)){if(i.el&&(m=O,O=S,S=m,o=l,l=g,g=o),o=l+g,m=[];o--;m.push(0));for(w=N,v=A,o=g;--o>=0;){for(r=0,p=S[o]%v,d=S[o]/v|0,s=l,u=o+s;u>o;)c=O[--s]%v,h=O[s]/v|0,f=d*c+h*p,c=p*c+f%v*v+m[u]+r,r=(c/w|0)+(f/v|0)+d*h,m[u--]=c%w;m[u]=r}return r?++i:m.shift(),I(e,m,i)},T.toDigits=function(e,n){var t=new a(this);return e=null!=e&&H(e,1,E,18,"precision")?0|e:null,n=null!=n&&H(n,0,8,18,v)?0|n:k,e?U(t,e,n):t},T.toExponential=function(e,n){return _(this,null!=e&&H(e,0,E,19)?~~e+1:null,n,19)},T.toFixed=function(e,n){return _(this,null!=e&&H(e,0,E,20)?~~e+this.e+1:null,n,20)},T.toFormat=function(e,n){var t=_(this,null!=e&&H(e,0,E,21)?~~e+this.e+1:null,n,21);if(this.c){var r,i=t.split("."),o=+X.groupSize,u=+X.secondaryGroupSize,s=X.groupSeparator,f=i[0],l=i[1],c=this.s<0,a=c?f.slice(1):f,h=a.length;if(u&&(r=o,o=u,u=r,h-=r),o>0&&h>0){for(r=h%o||o,f=a.substr(0,r);h>r;r+=o)f+=s+a.substr(r,o);u>0&&(f+=s+a.slice(r)),c&&(f="-"+f)}t=l?f+X.decimalSeparator+((u=+X.fractionGroupSize)?l.replace(new RegExp("\\d{"+u+"}\\B","g"),"$&"+X.fractionGroupSeparator):l):f}return t},T.toFraction=function(e){var n,t,i,o,u,s,f,l,c,h=j,g=this,p=g.c,d=new a(q),m=t=new a(q),w=f=new a(q);if(null!=e&&(j=!1,s=new a(e),j=h,(!(h=s.isInt())||s.lt(q))&&(j&&L(22,"max denominator "+(h?"out of range":"not an integer"),e),e=!h&&s.c&&U(s,s.e+1,1).gte(q)?s:null)),!p)return g.toString();for(c=r(p),o=d.e=c.length-g.e-1,d.c[0]=R[(u=o%y)<0?y+u:u],e=!e||s.cmp(d)>0?o>0?d:m:s,u=z,z=1/0,s=new a(c),f.c[0]=0;l=C(s,d,0,1),i=t.plus(l.times(w)),1!=i.cmp(e);)t=w,w=i,m=f.plus(l.times(i=m)),f=i,d=s.minus(l.times(i=d)),s=i;return i=C(e.minus(t),w,0,1),f=f.plus(i.times(m)),t=t.plus(i.times(w)),f.s=m.s=g.s,o*=2,n=C(m,w,o,k).minus(g).abs().cmp(C(f,t,o,k).minus(g).abs())<1?[m.toString(),w.toString()]:[f.toString(),t.toString()],z=u,n},T.toNumber=function(){var e=this;return+e||(e.s?0*e.s:0/0)},T.toPower=T.pow=function(e){var n,t,r=m(0>e?-e:+e),i=this;if(!H(e,-S,S,23,"exponent")&&(!isFinite(e)||r>S&&(e/=0)||parseFloat(e)!=e&&!(e=0/0)))return new a(Math.pow(+i,e));for(n=J?d(J/y+2):0,t=new a(q);;){if(r%2){if(t=t.times(i),!t.c)break;n&&t.c.length>n&&(t.c.length=n)}if(r=m(r/2),!r)break;i=i.times(i),n&&i.c&&i.c.length>n&&(i.c.length=n)}return 0>e&&(t=q.div(t)),n?U(t,J,k):t},T.toPrecision=function(e,n){return _(this,null!=e&&H(e,1,E,24,"precision")?0|e:null,n,24)},T.toString=function(e){var n,t=this,i=t.s,o=t.e;return null===o?i?(n="Infinity",0>i&&(n="-"+n)):n="NaN":(n=r(t.c),n=null!=e&&H(e,2,64,25,"base")?D(l(n,o),0|e,10,i):B>=o||o>=$?f(n,o):l(n,o),0>i&&t.c[0]&&(n="-"+n)),n},T.truncated=T.trunc=function(){return U(new a(this),this.e+1,1)},T.valueOf=T.toJSON=function(){return this.toString()},null!=e&&a.config(e),a}function t(e){var n=0|e;return e>0||e===n?n:n-1}function r(e){for(var n,t,r=1,i=e.length,o=e[0]+"";i>r;){for(n=e[r++]+"",t=y-n.length;t--;n="0"+n);o+=n}for(i=o.length;48===o.charCodeAt(--i););return o.slice(0,i+1||1)}function i(e,n){var t,r,i=e.c,o=n.c,u=e.s,s=n.s,f=e.e,l=n.e;if(!u||!s)return null;if(t=i&&!i[0],r=o&&!o[0],t||r)return t?r?0:-s:u;if(u!=s)return u;if(t=0>u,r=f==l,!i||!o)return r?0:!i^t?1:-1;if(!r)return f>l^t?1:-1;for(s=(f=i.length)<(l=o.length)?f:l,u=0;s>u;u++)if(i[u]!=o[u])return i[u]>o[u]^t?1:-1;return f==l?0:f>l^t?1:-1}function o(e,n,t){return(e=c(e))>=n&&t>=e}function u(e){return"[object Array]"==Object.prototype.toString.call(e)}function s(e,n,t){for(var r,i,o=[0],u=0,s=e.length;s>u;){for(i=o.length;i--;o[i]*=n);for(o[r=0]+=O.indexOf(e.charAt(u++));rt-1&&(null==o[r+1]&&(o[r+1]=0),o[r+1]+=o[r]/t|0,o[r]%=t)}return o.reverse()}function f(e,n){return(e.length>1?e.charAt(0)+"."+e.slice(1):e)+(0>n?"e":"e+")+n}function l(e,n){var t,r;if(0>n){for(r="0.";++n;r+="0");e=r+e}else if(t=e.length,++n>t){for(r="0",n-=t;--n;r+="0");e+=r}else t>n&&(e=e.slice(0,n)+"."+e.slice(n));return e}function c(e){return e=parseFloat(e),0>e?d(e):m(e)}var a,h,g,p=/^-?(\d+(\.\d*)?|\.\d+)(e[+-]?\d+)?$/i,d=Math.ceil,m=Math.floor,w=" not a boolean or binary digit",v="rounding mode",b="number type has more than 15 significant digits",O="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ$_",N=1e14,y=14,S=9007199254740991,R=[1,10,100,1e3,1e4,1e5,1e6,1e7,1e8,1e9,1e10,1e11,1e12,1e13],A=1e7,E=1e9;if(a=n(),"function"==typeof define&&define.amd)define(function(){return a});else if("undefined"!=typeof module&&module.exports){if(module.exports=a,!h)try{h=require("crypto")}catch(D){}}else e.BigNumber=a}(this);
-//# sourceMappingURL=doc/bignumber.js.map
diff --git a/signer/rules/deps/bindata.go b/signer/rules/deps/bindata.go
deleted file mode 100644
index 401dfca5a9..0000000000
--- a/signer/rules/deps/bindata.go
+++ /dev/null
@@ -1,244 +0,0 @@
-// Package deps Code generated by go-bindata. (@generated) DO NOT EDIT.
-// sources:
-// bignumber.js
-package deps
-
-import (
- "bytes"
- "compress/gzip"
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
- "time"
-)
-
-func bindataRead(data []byte, name string) ([]byte, error) {
- gz, err := gzip.NewReader(bytes.NewBuffer(data))
- if err != nil {
- return nil, fmt.Errorf("read %q: %v", name, err)
- }
-
- var buf bytes.Buffer
- _, err = io.Copy(&buf, gz)
- clErr := gz.Close()
-
- if err != nil {
- return nil, fmt.Errorf("read %q: %v", name, err)
- }
- if clErr != nil {
- return nil, err
- }
-
- return buf.Bytes(), nil
-}
-
-type asset struct {
- bytes []byte
- info os.FileInfo
-}
-
-type bindataFileInfo struct {
- name string
- size int64
- mode os.FileMode
- 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
-}
-
-// ModTime 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 fi.mode&os.ModeDir != 0
-}
-
-// Sys return file is sys mode
-func (fi bindataFileInfo) Sys() interface{} {
- return nil
-}
-
-var _bignumberJs = []byte("\x1f\x8b\x08\x00\x00\x00\x00\x00\x00\xff\x94\xbc\x6b\x77\x9b\xc8\x93\x38\xfc\x7e\x3f\x85\xc4\xc6\x9c\x6e\x53\x20\x90\x9d\x38\x86\x14\x9c\x4c\x62\xe7\xe7\x79\x1c\x3b\x4f\x9c\xcc\xcc\xae\xa2\xc9\x91\x51\x23\x75\x82\x40\xe1\x62\xc7\x09\xfe\x7d\xf6\xff\xa9\x6e\x40\xf2\x25\xbb\xb3\x6f\x2c\xe8\x4b\x75\x75\x75\xdd\xbb\xf0\x68\x77\x70\x29\x17\x59\xbd\xba\x14\x85\xf3\xa5\x1c\x5c\x8d\x1d\xd7\xd9\x1b\x2c\xab\x6a\x5d\xfa\xa3\xd1\x42\x56\xcb\xfa\xd2\x89\xf3\xd5\xe8\xad\xfc\x2a\xde\xc6\xe9\x68\x7b\xf8\xe8\xf4\xe4\xd5\xd1\xd9\xab\xa3\xc1\xee\xe8\x3f\x46\xbb\x83\x55\x3e\x97\x89\x14\xf3\xc1\xe5\xcd\xe0\x87\x48\xe5\x62\x50\xe5\x83\x44\x7e\x7f\x0c\x5c\x91\x5f\x8a\xa2\xfa\x5a\xc8\x95\xc8\x46\x79\x55\xe5\xff\x59\x88\x45\x9d\xce\x0a\x5b\x7c\x5f\x17\xa2\x2c\x65\x9e\xd9\x32\x8b\xf3\xd5\x7a\x56\xc9\x4b\x99\xca\xea\x86\x96\x19\x26\x75\x16\x57\x32\xcf\x98\xe0\x3f\x8d\xba\x14\x83\xb2\x2a\x64\x5c\x19\x41\xd7\x31\x50\x5d\xfd\xdb\x8c\x09\xc8\xf8\xcf\xab\x59\x31\xa8\xa0\x00\x09\x39\xd4\x50\x42\x82\xd5\x52\x96\x81\x4c\xd8\x90\x25\x03\x99\x95\xd5\x2c\x8b\x45\x9e\x0c\x66\x9c\x17\xa2\xaa\x8b\x6c\xf0\xc5\x34\x4f\xd9\xf8\x19\x18\x71\x9e\x95\x55\x51\xc7\x55\x5e\x0c\xe2\x59\x9a\x0e\xae\x65\xb5\xcc\xeb\x6a\x90\x89\x6b\x03\x04\x87\x4c\x5c\xb7\xeb\x10\xc0\xac\x4e\xd3\x21\x66\xa6\xf9\x2f\x96\xc1\x18\x9e\xed\xc3\x5b\x30\x2e\x67\xa5\x30\x38\xff\x49\xfd\xe8\x36\x19\x94\x28\x2c\xc3\x00\xcf\x45\xcc\xba\x15\x13\x6c\x21\xdd\x41\x28\x12\x7e\xc9\xe1\x23\x4b\xe0\x9d\x95\x38\xc2\xf2\xe0\xab\x5a\x87\xe5\x68\xe8\xa3\x30\x10\xab\x9b\x35\x0d\x16\xdc\x34\xdd\x5d\x31\x44\xb7\x69\x86\x04\xec\xbd\x58\x1c\x7d\x5f\x33\xe3\x6f\x3b\x32\x2c\x56\xa1\x31\x31\xac\x73\xa7\x4c\x65\x2c\x98\x0b\x19\xb7\x8c\xa9\x65\x70\xcb\x60\x91\xff\xe9\x93\x63\x58\x95\x65\xf0\xe8\x89\x01\x7b\x07\x61\x16\x19\xd2\xf0\x0d\x83\x3b\x95\x28\x2b\x56\xf6\x84\x59\xb0\x04\x4a\xc8\x69\xbb\x79\xc4\x12\xa7\x44\x37\xf4\x46\x22\x62\x25\x96\x2d\x68\x8f\x83\xed\x71\xdf\x83\x2f\xa6\x59\x3a\x85\x58\xa7\xb3\x58\xb0\xd1\xdf\xee\x27\xc7\xdd\x6d\x3e\x39\x23\x20\xb8\xa9\xc8\x16\xd5\x32\xf4\x9e\x12\xa5\xdf\xc2\x25\xd1\x32\xc7\xa1\xc7\x7d\x02\xba\xff\x14\x11\x4b\x27\x5e\xce\x8a\x57\xf9\x5c\xbc\xac\x98\xcb\x1f\x5d\xa3\xc4\xd7\xac\x04\xcf\x85\x0c\x12\xa7\xe4\xb7\x22\x2d\x05\x11\xfa\x2e\x19\x7b\x22\x3b\x25\x0a\xa7\x84\xc4\x11\x28\x1c\x01\x89\x13\x23\xa3\xc7\x98\x47\xa2\x05\xcd\x7d\x01\x57\xb9\x9c\xb3\xb7\xe8\xfe\x6f\xb4\x46\x74\xd5\xb1\x6e\xd1\x41\xa0\x2d\x5a\xdc\x04\x22\xfe\xfb\xdf\xc4\x90\x79\xc1\x0a\x74\x41\xa2\x08\x64\x88\x9e\x1b\xc8\x11\x7a\x2e\x14\x96\xc5\x83\x1e\x35\x81\x85\x42\x68\x22\xa6\x1b\x04\x6e\x35\xaf\xf4\xfb\x1a\xae\xdb\x13\x51\xcd\xf7\x8f\x85\x07\xff\x17\xe2\xdd\xde\x12\x62\xac\xc0\xd2\x91\xd9\x5c\x7c\x3f\x4f\x98\xe1\x18\x9c\x87\xb6\x67\x9a\x6a\x7c\x77\x78\x86\x63\xd0\xa1\x71\x60\x92\xa0\x88\x59\x11\x2f\xd9\x48\x8c\x24\xe7\xa1\x1b\x31\x37\x2c\x4c\x93\x15\x28\x39\x14\x16\x5a\xdd\x3a\xd2\xf2\x38\xa8\x65\xeb\x4b\x92\xd4\x6c\xc1\x5c\x90\x9c\xfb\xdd\xf8\xb2\xe5\x02\x0e\x12\xdd\x60\xff\xf9\x7d\xb4\x25\x0f\x24\x91\x88\xd0\xac\xfb\xd1\x8f\x0c\xb4\xed\x9a\x07\xea\xb0\x36\xbb\x94\x50\x5b\x1e\xe7\x32\xd9\x9a\x0a\xb9\x69\x7e\x31\xcd\x7a\x8b\xed\x12\xa7\xdc\x15\x1c\x0a\x2c\x6c\x69\x7b\x50\x84\x3f\x38\x1d\x02\x1d\x07\x09\x73\x40\x84\x1f\xc8\x84\xbd\x09\x0b\xd5\x31\xa1\x1e\x77\x1a\x74\x07\xb2\x75\x6e\x53\x90\xc8\x0a\xcb\xe3\x3b\x37\xa0\xb7\x28\x2d\xbc\xe1\x50\x87\x52\xf3\x80\x34\xcd\xc4\x89\x9d\x75\x5d\x2e\x59\x4f\x25\x45\x12\xa8\x6d\xbc\x09\xea\x50\x06\xfc\xe1\x08\x09\x0a\x0e\x0f\xb6\x36\x47\x24\xbb\xb1\xbb\x7d\xdd\x6a\x2c\x6d\xac\x15\xad\x02\x69\xdb\x41\x69\xa1\xe1\x1a\xc4\x11\x3d\x3c\x2d\x1e\x83\xed\x6d\xbc\x45\xf7\xb6\xd7\x97\xaf\x49\x8f\x41\x05\x52\xeb\x4c\xd2\x96\x09\xc4\xb0\x84\x05\xac\x61\x8e\xe2\x0e\x9b\xc0\x0a\xdf\xc1\x35\x7e\x55\x2b\xee\x1d\x84\x95\x69\x2a\x51\xaa\xf2\xd3\xfc\x5a\x14\xaf\x66\xa5\x60\x9c\xc3\x3c\x44\xd7\x34\x59\x82\xbf\xc3\xef\xe8\x02\x8d\xb8\xc7\x55\xb0\x6e\x55\x5f\xc5\x61\x89\x6b\x67\x9d\x5f\x33\xd1\x6e\xcc\x9e\x73\xf8\x1d\x13\x58\x3b\x31\x96\x2c\x65\x05\x5b\x3a\x31\x87\xa5\x23\xb8\x12\x7a\x0e\x6b\x47\xe0\xda\x89\x7b\x4e\x5a\x60\xc9\x04\x54\xd4\x55\x63\x82\x8b\x8e\x69\x5c\xc4\xc5\xc4\xb6\x93\x69\xb0\x70\xd6\xf9\x9a\x71\xc5\x2e\xc3\xc5\xc4\x9d\xb6\x42\x64\xb8\x06\x35\xb9\xe1\x3c\xb2\xed\xda\xa7\x95\x70\x41\x4b\x61\x0d\x4b\xa7\x44\x09\x4b\x7c\xc5\x96\xb0\x86\x15\x5c\x13\xfc\x05\x2e\x9d\x18\x62\x5c\x3a\x05\xd4\xa8\x70\xca\xb1\xb6\x56\x96\x07\x73\x5c\x4c\xf2\x29\x24\x98\x8d\xc6\x10\x63\xdc\x34\x6e\x98\x37\x8d\x36\x0f\x8b\x49\x6e\x79\x53\x88\x71\x3f\xbc\x8e\x5a\x93\x31\x6f\x9a\x98\x9b\x26\x73\x11\xaf\x9b\xe6\x1a\x91\x2d\x9d\xf2\x85\x1b\xed\xf9\x63\xce\xfd\x79\x98\x34\xcd\x1c\x31\x31\x4d\xb6\xaf\x46\xc4\x4d\xf3\x0c\xf1\xda\x34\x3d\x73\x31\xc9\x6d\x6f\xba\x3d\xe9\xb9\x7f\xc0\x39\x78\xb4\xa2\xde\xa0\xc0\x38\x4a\x99\xe1\x19\x60\xaf\xb8\x4f\x1b\xed\xd8\xb7\xa3\x0f\xe6\x10\x73\x3a\x49\xdb\xce\x02\xcb\x22\x52\xe5\xd3\x30\x0b\x38\xed\x03\x5d\xc8\x9b\x86\x59\x56\x0d\x0b\xa7\xce\xca\xa5\x4c\x2a\xe6\x71\x2d\x98\x5b\x34\x1e\xb6\x14\xd6\x1d\x73\x75\xdc\x86\x11\x24\x21\xce\x03\x61\xe1\xb9\x12\xd9\x97\x15\x5b\x4c\xe6\x96\x35\xe5\x3c\x10\x98\x32\x01\x35\xbf\x6d\xd5\x98\xd8\xf0\xe2\xe7\x87\xbc\x58\x12\x2f\xd2\x11\x55\xa8\x89\x56\x91\x9d\xad\xc0\x85\xe7\x20\xe1\x8a\x47\x6e\x53\xf9\x5f\x61\x48\xea\xbc\x03\xe8\x54\xf9\x85\x56\x3d\xea\xbc\x73\xd2\xf5\x13\x77\x4a\x26\xd8\x11\x40\x60\xc8\x06\x2f\xb1\x60\x42\x31\x16\x7a\x87\x88\xb2\x69\xc6\xfb\x88\xd2\x34\x7f\x0b\xb1\x8c\x12\xb6\x84\x92\xfb\xa9\xfa\xe9\x15\x82\xc0\x8f\xac\x35\xd9\x9c\x30\x25\x7e\x23\x98\x3d\x2c\x62\x8c\x56\xed\xdc\x05\xca\xea\x10\xb3\xa6\xf9\x2d\xc4\x9a\x6b\xc5\x10\x64\x61\x1c\x2c\x95\xc0\x42\x4c\x1a\x6f\x89\xb4\x68\xdd\x0a\x2c\x39\x0e\x36\x96\xb0\xc4\x54\xb5\x92\x66\x0b\x63\x65\x79\x6c\x3b\x0b\x5d\x75\x70\x34\xdd\x31\x82\xcc\xb6\x5b\x48\x3c\xd8\xcc\xb6\xb0\xb6\x63\xe8\x86\xd6\x96\x87\x18\x9b\x66\x3b\x87\xdf\x99\xd4\x53\xae\x7c\xe1\x9a\x66\x1e\x19\xb6\x61\x2d\xfd\xe5\xe6\x64\xbe\xdf\xf3\xaa\xd0\xd5\x0a\x9a\x09\x62\x35\xad\x05\xe8\x09\xaa\xce\xa5\xa1\xb7\xc0\xb2\xe4\x8b\x4e\xac\x03\x85\x7b\xd1\xf7\xcb\x29\x87\x61\xe1\x94\xfc\x67\x85\x45\x70\x59\x88\xd9\xd7\xdb\xcc\x21\x7f\x8b\x55\x50\x10\xcc\x0a\x8b\x9e\x4b\xaa\x0d\x2e\xc7\x2d\x97\x14\xc4\x27\xba\x9b\x65\xa1\x68\x1a\x11\x56\x4d\x23\x86\x18\x33\xc1\x39\xe9\xfa\x02\x98\x6c\x1a\x63\x2e\x62\xb9\x9a\xa5\x03\xa5\x81\x4a\x83\x5b\xfd\xf0\xc8\x18\x90\x5f\x97\x27\x83\x62\x96\x2d\x84\xe1\x1b\x83\x2c\xaf\x06\xb3\x6c\x20\xb3\x4a\x2c\x44\x61\x70\xf2\x51\x86\x5b\xfa\xf2\x44\xaf\xae\xcf\x90\xe8\x51\xa0\x07\x12\xb3\x5e\x1e\xb2\x89\x6d\xcb\x69\x90\x75\x1a\x47\x19\x01\xcc\x26\xee\xf4\x57\x7e\x00\x6d\xd4\xaa\x76\x6f\x6c\x8f\x87\x3f\x22\xe1\xc4\xc4\x53\x8a\xdd\xfd\x37\x61\xa5\x1a\x26\x42\xa9\x6e\x9f\xd1\x6f\x05\xd4\x94\x71\xd8\x12\x9d\xd3\x0e\x2d\x8d\x12\x11\xf9\xa8\x28\xf2\x82\x4d\x0c\x7a\xfe\x4d\x2e\xce\xb4\x3b\x03\x46\xbc\x5a\x1b\xca\xc9\x4d\xe4\xc2\x00\x63\x2e\xaf\xf4\xdf\x0f\xf9\x49\x56\x19\x60\x88\x6f\x06\x18\x8b\x4a\xfd\x11\x06\x18\x69\xa5\xfe\xd0\xe3\x4a\x66\x75\x49\xbf\xf9\xdc\x00\x63\x9d\xaa\x97\x75\x21\x62\x49\xfe\xbb\x01\x46\x31\xcb\xe6\xf9\x8a\x1e\xf2\x3a\xa3\x31\x4a\x6f\x18\x60\x54\x72\x25\x68\x70\x95\xbf\x96\x0b\x59\xe9\xc7\xa3\xef\xeb\x3c\x13\x59\x25\x67\xa9\x7a\x3f\x96\xdf\xc5\x5c\x3f\xe5\xc5\x6a\x56\xe9\xc7\x62\xa6\xb6\x48\x2b\xe5\xd7\xaa\xe9\xdd\xd6\x8a\x9d\xac\x1b\x60\x6c\x36\x39\x9d\x88\xa9\x65\x30\x3e\x30\xac\xcc\x32\xfc\x81\x61\x55\x3c\xa8\x96\x45\x7e\x3d\x28\x9c\x6c\xb6\x12\xb8\x19\xac\xe9\x64\xc0\x5b\x74\xa1\xd8\x10\xf4\x63\xc7\x65\x9a\xa4\x7d\x1c\x01\x29\xc4\x30\x23\x95\x02\x4b\x7c\x4f\xfa\x65\xc6\x7f\x0a\x5f\xdb\x7a\x24\xe7\x74\x46\x47\x5d\xaa\xa3\x2e\xd5\x51\x2b\x7f\x46\x29\xa2\xcc\x96\xe0\x86\x39\xcf\x2d\xbc\x81\x1a\x33\x48\x70\x36\x49\xd1\x25\xc3\x90\x8c\x96\x13\x69\xd7\xb6\x37\xdd\xf1\xdc\xc6\xed\x75\x4e\x8a\x73\xc6\x72\xcb\xe3\xa3\x1b\x0e\x69\x88\xb3\xce\xec\x29\xd7\xb0\xe0\x4a\x72\x06\x42\x3b\x01\x5d\xe7\x0b\x4c\x83\x99\x76\x01\x5c\xe2\x41\x8c\x95\x2b\xea\x41\xbe\xa3\x56\xce\xed\x1b\xcb\xd3\x0e\xa6\xd6\xe7\x84\x76\x4a\xce\x8c\xf7\x10\xf5\xad\x39\x12\x62\x74\xc3\x3a\x72\xfd\x7b\xe8\xde\x2a\xd9\x2e\xc8\xe6\x65\x9d\xcd\x9b\x4d\x52\x8b\x8c\x14\xa3\x19\x89\x9f\xec\x74\x33\xc8\xf5\xda\x0f\xab\x88\xc5\x4d\x53\xb4\x16\xb0\x6a\x9a\x0a\x91\x89\x2d\x0b\x18\x87\x4f\x9b\xe6\xa9\xd6\x5a\xfb\x6a\x44\xa1\x2c\x20\x79\x1d\x79\xe8\x46\x75\xe8\x46\x2d\x1a\x53\xdf\xf5\x67\x93\x94\x60\xef\x78\xae\xe9\x6d\x03\xeb\x2c\x63\xd6\x34\xc3\xd9\xc6\xf4\x0f\x3a\x5a\xd1\xb9\x47\xa4\x6c\x85\x0a\xb6\x68\x08\x2e\x27\xd9\xce\xcd\x14\x48\xda\xec\xac\x69\x5c\xee\xab\x66\x25\x85\x20\x94\xcb\x80\x98\x47\xac\x87\x91\x42\x89\x1e\xa4\xb6\xcd\xfd\xad\x46\x8b\xf8\x61\x39\xb9\xb1\xf3\x29\x10\x7d\x91\x50\x5e\xb1\x0e\xe9\x9d\xe5\xa4\x9e\xf2\xdd\xd2\x77\x39\x14\x4a\x4b\x07\x5a\x4b\xba\x88\xa9\xd6\x30\x39\x7a\x50\x6b\x96\xaa\xd5\xb9\xd4\xea\x5c\xf2\x8d\x8b\x4c\x7d\x16\x96\xb4\xfe\x9d\x21\xa5\x3a\xba\x21\x96\xa4\x9d\x1d\x61\x59\x7a\x67\x78\x66\x9a\x4c\x3d\x91\x31\xd7\x6a\x97\x98\x78\x92\x2a\x28\xf4\x3b\xc4\x33\xcd\x55\x01\x91\xd4\x26\x57\xa0\x44\xef\x56\xa3\x33\xdb\x72\xae\x70\xa6\x5c\x06\xe2\x34\xad\xeb\x6e\x85\x23\xee\xab\x30\xe1\x88\x17\x6f\x14\x0e\xbd\x1a\xdb\xb2\xfd\x24\x5b\xaf\x94\xec\x7d\xc0\x99\xb3\x2e\xf2\x2a\xa7\x70\x0b\xbe\xb5\x76\xc2\xe3\xf0\x0e\xc7\x2e\x7c\xc5\x7d\xf8\x0d\xed\x03\x78\x82\x63\x0f\xde\xa0\xed\x89\x03\xf8\x81\xf4\xf7\x0b\x0e\x5d\xf8\x17\x1e\xc3\x1f\x38\xf4\xe0\x4f\xf4\xe0\x77\xf4\x5c\x17\xfe\xc2\x9f\xad\xe6\xbf\x10\xeb\x59\x31\xab\xf2\xc2\x27\xf7\x73\x51\xe4\xf5\x7a\xab\x09\xba\x26\xf9\x43\xf8\x7b\x50\x8a\x38\xcf\xe6\xb3\xe2\xe6\x4d\xdf\xe8\x42\xd2\x2a\xa1\x37\xf7\xe6\x0e\x8c\x7b\x5d\x6a\xf8\x6d\xd0\xb3\xd8\x2c\xcb\xab\xa5\x28\x30\x83\x99\xf3\xfe\xfc\xe3\xd9\xeb\xcf\x1f\xdf\xa1\xdb\xbf\xbc\x3e\xff\xf3\x0c\xbd\xfe\xf5\xd5\xd1\xc9\x29\x8e\xfb\xd7\xe3\xd3\xf3\xf3\xf7\xb8\xd7\xbf\xff\xeb\xe5\xe9\x31\xcd\xdf\xbf\xdb\xa2\x80\x3c\xbd\xdb\x76\xf4\xc7\xd1\x19\x3e\xbb\xdb\xa6\xa0\x1f\xdc\x6d\xd3\x4b\x3c\x87\x99\x73\xf4\xf1\xd5\xe9\xc9\x6b\x3c\x84\x99\xa3\x6d\x03\xf6\xa9\x17\xad\x02\x95\x3e\x24\x61\xc1\x9f\xb7\x20\x71\x56\x2c\xea\x95\xc8\x2a\xe2\x3c\x49\xee\x55\x42\xac\x66\xe4\x97\x5f\x44\x5c\x6d\xa2\xe6\x32\xda\x02\xd3\x92\xa5\x74\x96\xb3\xf2\xfc\x3a\x7b\x57\xe4\x6b\x51\x54\x37\x2c\xe3\x91\x56\x19\x4c\x60\x39\xc9\xa6\xdc\xa7\x60\x78\xe0\xde\xfa\x0f\x27\xcb\x2e\x8d\x50\x6d\xe6\xc8\x49\x45\xce\x65\x37\xab\x8f\xaf\x59\x86\xc6\xeb\xa3\x57\x27\x6f\x5f\x9e\x7e\x7e\x77\xfa\xf2\xd5\xd1\x85\xc1\xc9\x7f\x14\xe0\xc2\x11\x8c\x21\x23\xe5\xf3\x0e\xdd\x86\xa2\xc1\x49\x36\xc5\x77\xa0\xe6\x28\x02\x9d\x9c\xbd\xf9\xfc\xf6\xfc\xf5\xd1\x66\xca\xf3\x6e\xca\xd7\xad\x29\x5f\xf5\x94\xa3\xbf\xde\x9d\x9f\x1d\x9d\x7d\x38\x79\x79\xfa\xf9\xe5\x07\x9a\x43\xde\x11\x8f\xfe\xa5\x5c\x21\xb0\x8f\xc0\x6d\x67\x53\x8b\x37\xdd\xc6\xe0\x37\x02\x47\xa3\x9e\xa8\x07\x6f\xca\x7d\x5a\xd0\x3e\xda\x1e\x62\x33\xea\x65\x6e\x28\x22\x5b\xf8\x82\x73\xde\x22\x30\xf9\x0d\x9e\x4c\x5b\xbc\x5f\x9e\xbd\x39\x7a\x6c\x6d\xdb\xbb\xbb\xb8\xb7\x81\xfc\xa6\x5b\xfc\xc7\x2f\x17\x77\x1b\x11\xbd\x41\x9b\xfd\xb8\x8b\x80\xaf\x33\x66\x90\x59\xc6\x20\x9e\x65\xe4\x39\x5d\x8a\xc1\x0f\x51\xe4\x06\x88\x0d\x7a\x6f\xe0\x47\x8b\xde\xd1\xfb\xf7\xe7\xef\xd5\x11\x30\x81\x88\xc3\xa1\x68\x1a\x0f\x11\x45\xd3\x90\x36\x11\x11\x23\x45\xf0\x2f\x64\x5f\xa8\x8f\x47\xc7\x7e\xbe\xb5\xc8\x35\x01\xd5\x30\xbf\x68\x78\xaf\xde\xff\xd7\xbb\x0f\xe7\xff\x13\xbc\x3f\x70\xc8\xa8\x75\xb8\x6c\x9a\x8e\x35\x87\x1d\x6b\x2e\x39\x08\xd3\x1c\xfe\xa1\xf2\x03\xb4\x86\x11\x17\x37\xeb\x2a\x1f\xd4\xd9\xec\x6a\x26\xd3\xd9\x65\x2a\x0c\x58\xf2\xc7\x71\xf8\x43\xe3\xf0\xf6\xfc\xf5\xc7\xd3\xf3\x7b\x8c\x72\xd8\x51\xee\xcf\x2d\x46\xf9\x53\x4f\x78\x77\xfe\xe7\xe7\x77\xef\x8f\x5e\x9d\x5c\x9c\x9c\x9f\x3d\xc2\x8e\xbf\x6f\x4d\xf9\x5d\x4f\x39\x3e\x7f\xff\xb6\xe5\xa9\x07\xf2\x25\xa2\xbf\x50\x6c\x9f\x44\xeb\xc0\xb6\xe3\x36\xf8\xfe\x05\xc5\x2d\xcc\x9c\xd5\xec\x3b\x3e\x14\xaa\xef\x6c\x23\xce\x1f\x9c\xb4\xe2\x6a\xa8\xcc\xfe\xd7\xa1\x0b\x3d\x54\xfb\x7d\x0f\x34\x06\x1e\xba\xee\x81\x77\x78\x38\x7e\xba\x7f\xb0\xef\x1e\x1e\x8e\x21\xc3\xb7\xb3\x6a\xd9\x8e\x67\x7c\x57\x98\x63\xf7\xf0\xc0\x7b\xea\x3d\xa2\x26\x56\xec\xde\x58\xfe\x98\x3e\x78\xbe\xf7\xfc\xf9\x33\xf7\xf9\x2e\xf3\xdc\x83\xbd\x83\x7d\xef\xf9\x78\x7f\xf7\xce\xbc\xc6\xe5\x16\xeb\x46\xdd\xef\xd9\xe8\x8a\xad\x3c\xf3\xbd\xe4\x31\xba\x90\xe0\x64\x0a\x69\x6b\x93\xbe\x29\x6f\x4e\xb4\x01\xa9\xd8\x9c\xa0\xb7\x4f\xf1\xa8\xf0\xdf\x41\x8e\x73\x26\xc8\x61\xfb\x83\xcb\x84\x2d\x4d\x73\xe9\x2c\x44\xf5\x5e\xad\xfb\xc7\x2c\xad\x45\xa9\xcd\x7b\x85\x0f\x3a\x54\x80\xf9\x51\x66\xd5\xde\xf8\x65\x51\xcc\x6e\x58\xbe\x8b\x63\xce\x83\x3c\x2c\x03\x5e\xa3\xb7\xe7\xb9\x07\xe3\xdd\x6a\x52\x4e\x2d\x56\x4d\x4a\xcb\x9b\x86\x61\xe8\x79\x1c\xea\x10\x0f\x85\xf7\x34\x62\xc5\x3f\x00\x3a\xe6\x1c\x08\x06\x16\x24\xfa\x1a\x0e\x16\x4a\xfa\x59\xa2\x1d\xc7\x7a\xc7\x13\xde\x3e\x87\xd2\xc2\x31\x0f\x4a\xcc\x47\xe3\x3e\xb8\x54\x3b\xd2\x64\xfc\xed\xa6\xda\xde\xcd\x56\x23\x61\x7e\xd0\x23\x3e\x7e\xee\xed\x1f\xec\x1f\x1e\x3c\x3b\xf0\xdc\x67\x4f\x9f\xed\xb2\x3d\xcf\x24\x0c\xb8\xe5\xb9\x87\x87\x4f\x3d\xef\xd9\xf8\xe0\xe0\xe0\xd9\xae\xc6\xc5\xda\x1f\x1f\xee\x1f\x3e\x3b\x18\x1f\xea\x96\xf1\xd4\xf2\x9e\x1d\x1c\x1c\x8c\x3d\xfd\xbe\xd7\xee\x7e\x7f\xfa\xe2\x85\xf7\x8c\xeb\x97\xa7\xd3\x17\x2f\x9e\x73\x8b\x1e\x9f\x4d\x7b\x7a\xdc\xc5\xe9\x80\x3b\x71\xbe\xbe\x61\x15\x85\xf7\x8f\x6c\xf5\x40\x6f\xf5\x40\x6f\x55\xc9\x95\xb7\xff\x2b\xcd\xa0\xd2\x49\xa5\xf6\xdc\xda\x6d\x66\x8c\x03\x2d\x1b\xd6\xa6\xc9\x92\x49\x69\x59\x53\x6c\xc1\x07\xda\x83\x4a\x26\xb6\x5d\x4e\x41\x90\x57\x9d\x9b\xa6\x20\x6d\x8d\xef\x27\x37\xb6\x98\x42\x42\x47\xb2\x62\xf9\xa8\xe6\xbb\x35\x57\x3e\x16\x35\x05\x89\xf6\xb0\xa0\xb4\x6d\xae\x13\x56\x25\x4f\x70\x22\xfb\xac\xa4\x0e\x3f\x6c\xaf\x9d\xe2\xd2\x14\x9d\xb3\xe1\x20\x6d\xbc\xd1\x8b\x97\xca\x9b\x4c\xee\x7b\x93\xca\x55\xbc\x09\xc9\x53\xa4\xb1\x76\xd9\x3b\x68\xa9\x23\x50\x42\xea\xc4\x98\x40\x7a\x7b\xcb\x38\xbc\xda\x16\xf2\x3e\x5a\x12\x77\xc2\xcf\x3b\x82\xd3\xc5\xff\x24\x3e\x3b\x2f\x21\xc6\x6c\xf4\xb2\xd1\xe9\x03\x81\x7d\x02\x3e\x48\x6c\x3b\xe0\x39\x8a\x49\x32\xdd\x79\x09\xb5\x7a\xa0\x81\x50\x60\xbc\x9b\x5b\xf5\x6e\x0a\x12\xd3\xdd\xdc\x2a\x76\x5e\xee\xbe\xb4\xc8\xeb\x60\x72\x54\x29\xe1\x2e\x68\x20\xb7\xe2\xdd\x1a\x68\x1a\xca\x9d\xaa\x13\xeb\xd2\x34\x45\x9f\xbe\x2a\xef\x84\xcc\xd9\x83\x08\x4f\xe5\x99\x86\x58\xf0\x1c\xab\xb0\x88\x3c\xdf\xf6\x74\x18\xa6\xa9\x9b\xa3\x1b\x54\xa1\x54\xf9\x69\x52\x00\x13\x39\x1d\x62\x36\x91\x53\xfe\x93\x10\x97\xd3\x90\x5e\xf4\x34\xed\x58\xb7\x48\xe4\x9b\x45\x8b\xcd\xa2\x5d\x02\x41\x12\x58\xda\xbd\x98\x54\x53\x1b\x25\x48\xa4\xa7\x17\xd9\xa4\x22\x60\x2e\xd0\x1b\xca\xdd\xc2\x52\x03\xa8\x59\x07\x7b\x43\x32\xdb\xb4\xbf\xee\x5e\x25\x10\xdd\x99\xf3\xe0\xf6\xbe\x5e\xeb\x23\x58\xbd\xdd\x74\x93\xe4\x85\x6b\xb8\x82\x4b\x38\x87\x0b\x78\x0f\x2f\xe1\x08\x5e\xc3\x67\xf8\x0e\xc7\x28\x9d\x12\x31\x77\x4a\xb5\x25\x38\x41\xe9\xc4\x70\x8a\xb9\x13\xeb\x7b\xb4\x13\xd3\x3c\x51\x18\x9c\x9a\xe6\x29\x05\x56\x5d\x64\xa5\xd5\xa4\x74\x4a\xd3\xcc\xe9\x0f\x3b\x89\x86\xa7\x4d\x43\x83\x87\x48\x23\xfd\x53\x1e\x9d\x98\xa6\x8b\x48\x6d\x4d\x33\x3c\x8d\xdc\xdd\x63\xff\x78\xe4\xfa\xee\xc8\xd5\xbc\x7a\xd5\x6a\xdb\x63\x0e\x97\x78\xa5\x73\xed\x31\x4a\x47\xd8\xb9\x23\xe0\x18\x6b\x2b\xb6\x3c\x48\x9a\x86\x25\x78\x06\x31\x56\x4c\x3a\xa4\x72\xed\x8a\xe5\xea\x01\x8e\xf1\x78\x74\xd3\xb8\x1c\x96\xe8\x06\xa7\x93\xe5\x14\x91\x9d\x4c\x96\x53\x8a\xe7\x82\x65\x1b\x94\x53\x7b\xd8\x37\x9b\x66\x6c\xdb\xe0\x86\xc7\xfc\x52\x6b\x06\x8f\xc3\x02\x87\xee\x46\xc8\x8e\xf0\xa4\x63\xe8\xcf\x78\xda\x3d\x52\x10\x79\x6c\xe1\x18\xd6\x48\xe1\x1d\xa3\x4d\x5a\x1e\xe7\xb0\x0e\x3d\xd3\x64\xa7\x28\xd8\x29\xac\x21\xe1\x70\x82\x82\x9d\xe8\xc7\xad\xf9\x1b\xa8\x1c\x5e\xe2\x67\x38\xc7\x93\xfe\xaa\xe0\x33\x87\x0b\x3c\xef\xc2\xae\xcf\xe1\x45\x70\x3e\xb9\x20\xb5\xe2\xf2\xe0\x3b\x9e\x76\x12\x04\xdf\x7b\x3e\x77\x39\xbc\x56\x74\x86\xd3\x89\x37\x0d\x31\x19\x8d\x4d\xf3\xb5\x65\x05\xf3\x7c\xb0\x46\x97\x24\x91\x9d\xc2\x39\x7c\x86\x0b\x0e\x6e\x98\x46\xec\x3d\x9e\xd3\xf0\xcf\x43\xbc\x30\x4d\xf6\x1e\xdf\xef\x26\x16\x3b\x9f\x78\x8a\x28\x5c\xed\xea\xfd\xe8\xb5\xda\x4e\xc4\xd6\xa1\x4a\x4a\xaf\x31\xb1\x3d\x0e\xf3\xcd\xde\xae\x71\xde\x6d\x68\x83\xb1\x5a\x6d\x0e\xe7\x70\x4d\xab\x79\x88\x29\xcd\xb5\x6d\x28\xd8\x1c\xae\xc3\xcf\xd1\x77\xff\x14\xae\x21\xe1\x9c\xfb\x14\xf8\xae\x4d\x93\xa5\xb8\x46\x05\xba\xdf\xdd\x5d\xe0\xe1\xb5\x69\xce\xb7\xb7\x5b\xb0\x73\x98\xc3\x05\x21\x61\xb7\x4b\xdc\xc3\xa0\xdf\xaf\x17\x2a\x04\x2c\x4b\x4d\xba\x68\x11\xb8\x50\x08\x6c\xa1\xcd\x7d\xd2\xa4\xdd\xd0\x73\x54\xd9\xcd\xcb\xc9\x92\x08\xbf\x86\xd4\x34\x89\x60\x51\x7b\x12\x27\x93\x97\x44\x29\x9f\x9d\xe3\x84\x9e\xa7\x70\x81\x1e\x0f\xae\x97\x32\x15\x8c\xbd\xb4\xac\x17\x47\x5d\x52\xe4\x5c\x27\x4c\x8f\x49\x91\x2f\x70\xd3\x06\x97\x4a\x12\x2e\x3b\x09\xa6\xa0\x3c\x41\x3c\xd3\x7a\x62\x89\x1e\x1c\x23\x0d\x09\x8e\x95\xe2\x3e\x56\x8a\x5b\x31\xf1\x47\x76\x05\xb5\xc5\xae\x1c\x81\x4b\x2b\x56\x69\x44\xcb\x83\x12\x16\x6d\x26\x99\x3a\x62\xb8\x72\x0a\xb4\x16\x9d\x5a\xbc\x52\xba\xfc\x61\x88\x87\xa3\xbf\x99\x1d\x71\x97\x4d\xbe\x5f\xe6\x53\xce\x3e\x5d\x4f\x3e\x5d\x3b\xd3\xdd\x27\x7c\x24\x21\xa3\xde\xc9\xdf\xce\xd4\xe2\x9f\x9c\x27\x23\xa8\x70\xf4\xf7\x27\xa7\x6d\x79\x32\x82\x02\x47\x7f\xdb\x11\x3b\xc9\x12\x99\xc9\xea\xa6\x39\x9b\x9d\x51\xb3\xa4\x61\xe5\xee\x27\x8b\x29\x58\xbc\xf9\xfb\x53\x69\x35\x9f\x4a\xeb\xc9\x68\xf1\xc0\xfb\xba\xaf\xa3\xb0\x8c\x6a\xbf\xee\xaf\x8f\x24\x18\x4f\x3c\x43\x09\x6e\xa1\x2f\x45\x63\xce\x73\xa7\x44\x59\x9e\xcd\xce\x58\xac\xe3\x48\xdf\x0d\xe3\xc8\xf6\x7c\xaf\xbf\xf2\x18\x92\x16\x8a\x31\xee\x01\x09\xd8\x38\x7c\xda\x72\x75\x16\x0f\x8d\xef\x06\x22\xab\xb0\xba\x77\xad\x15\x79\xcf\x7c\xe3\x92\x3c\xef\x68\xec\x3f\x87\xc4\x34\x93\x21\xa6\x91\xf0\xb3\x5b\x4e\x6f\x2c\xc5\x04\xb6\xd7\xc8\x34\xb2\xfd\x7b\x05\x86\xeb\x50\x0b\x87\x7a\x88\xf1\x3d\x75\x19\x43\xca\x83\x2f\xfa\x8a\xd2\x50\x4e\xbc\x61\xb1\x24\x32\x06\x97\xb3\x52\x0c\x0c\x2b\xf1\x0d\x83\x93\x7f\xdf\xe6\x71\x6b\x0e\xb4\x71\xda\xef\x6d\xee\xc4\x98\xb7\x09\x17\x78\x8b\xae\x3a\xdd\x0f\xce\xec\xb2\xcc\xd3\xba\x12\xca\x07\x44\xf5\xfe\xf0\xc4\xdb\x7b\xb8\xa5\x2c\xef\xdf\x03\x30\xe1\x94\x24\x86\xe2\x16\x3e\x38\xb1\x90\xe9\x23\xd1\x40\x77\x1f\xa2\xe6\x03\xfd\x55\x49\xb4\x31\x57\x73\xf2\xd5\x7a\x56\x88\xf9\x87\x1c\x3f\x38\xf1\x6a\x8d\xdb\x34\xef\x41\xbc\x45\x0f\xa4\x02\xb0\x55\x58\xa1\xe6\xb7\xe9\x9b\x77\x2a\x6f\x8f\x1f\x9c\xf9\xfa\xb1\x9c\x44\xa1\x4a\x3b\x5a\xa3\x54\xf4\x44\xad\xd3\x54\xbb\xe9\x8c\x65\x58\x74\x77\x8b\x1e\xd9\x07\x8d\xe6\xe8\x86\xf3\xdd\x1b\xc8\x90\xc2\x23\xed\xc3\x65\x3b\x9e\x8b\xe8\x06\x99\x92\x2e\x41\x32\xda\x82\x73\x43\xa1\xa2\x4c\xb7\x25\xc7\x5c\x5e\xc9\xb9\x98\xff\x76\x83\xea\xf9\x57\x3b\xdb\x83\x57\xf7\x77\x06\xef\xe0\x2b\xdf\x02\xa1\xd2\xee\x62\x21\x8a\x0e\x96\x6a\xf8\x15\xc0\xfd\x47\x00\xba\xe0\x29\x80\xe2\x5b\x3d\x4b\x89\x4e\xe2\xdb\xaf\xa6\x3f\x05\xd2\x6a\x8f\x53\x3b\x49\xf3\xbc\xf8\xe7\x47\xbc\xa7\x26\x2d\x0a\x31\xab\x44\xf1\x61\x39\xcb\x90\xa2\xc1\x5f\x2d\xfc\xec\x91\x23\x0e\xdd\x7b\x10\xce\x8b\x23\xda\x82\x62\x97\x45\x25\x7e\x05\xeb\x80\xac\x08\xb2\xec\x91\x7d\x70\x1d\xf9\x67\x04\x58\x96\xc7\xa4\x87\xc4\xc3\x2d\x0d\x87\x9a\x63\xf4\xa8\x96\xfc\xd8\x3e\xff\x7a\xb8\x69\x6e\xb1\x4e\xa8\xdb\x3a\xbe\x1a\x6b\x58\x67\xb3\xb3\x47\xe6\xab\xa1\x65\x3b\x42\x2c\x66\x95\xbc\x12\xd8\xbe\x3c\x42\x70\x3d\xfc\x85\xab\x27\xfc\xb7\x28\xf2\xff\x09\x27\x17\x5b\xfe\x9f\xb8\x53\x9a\x91\x8a\xb2\x6c\x8f\x23\xfd\xe5\x71\x3c\x7f\xe4\x38\xf4\x82\xdd\xf4\xed\xb3\x48\x7f\x7d\x16\x87\xca\xde\xfe\xef\x87\xa1\x6e\x8e\xf0\x83\x53\xd6\x97\xf7\x40\xdd\x8d\x18\x14\x8c\x04\x4b\x47\xd5\x6a\xbd\x55\x62\x88\x5b\xbc\x9e\xa9\x5a\x9e\x61\xd2\x34\xc3\xec\xae\xfe\x54\x8e\x23\x19\xcd\xe1\xa6\xc0\x8a\x14\x98\x9d\x41\xe9\xac\xd3\xba\x64\x82\x07\xca\xaa\xa0\x3a\x41\x50\x39\xea\xd1\x0d\x2c\xb1\x74\x62\x58\xa0\x68\x55\x48\xda\x34\x43\x7d\xd1\x3a\x5c\x36\xcd\x70\xd1\x01\x5b\x46\xac\x85\x27\xb8\xaf\xd7\x5c\x44\xa5\xdf\xad\x3b\x5c\x6a\x57\x76\xab\xba\x60\x40\xcf\x0f\x67\xd1\xc0\xa8\xf4\xf7\x10\xbf\x46\xb6\xeb\xbb\xca\xd6\xa7\x58\xb1\x94\x2b\x3f\x56\xdd\x49\x2f\x7b\xbf\x2e\xc1\xd4\x8e\xb5\x1b\xc0\x6a\x74\xc3\x84\x47\x2c\x41\x3b\x81\x1c\x97\xdc\x67\x31\xa6\x90\xe3\x82\xac\x41\x21\xae\x44\x41\xb6\x0a\x32\x4c\xd4\x05\x6f\xbe\xb9\x03\xda\xea\xbe\xdd\x0a\x6a\x58\x8d\x2c\xe9\x6f\xad\xf9\x0b\x96\xf5\x77\xfb\x9c\x47\x89\x9f\x41\x82\x19\xba\x81\x0c\xb3\x20\xd3\x81\xcf\x72\x92\x4d\x87\xb8\x20\xad\xf9\xb3\x46\x7a\x7b\x41\x2f\x9b\xcb\x04\x0a\x7d\x73\x24\xaf\x78\x01\x0b\xcc\x41\x11\x40\x38\x25\xe1\xc5\xe4\x06\xbe\xad\x52\x15\x9d\xdf\xdb\xdd\x54\xeb\x9b\xe9\x49\xd1\xba\xb8\xd4\x94\xe1\x99\xed\x05\x32\x4c\xf4\xf5\xc8\x52\x5d\xb1\xbe\x58\xa8\xd0\x4b\x17\x5a\xc9\xa0\x30\xcd\x21\x75\x14\x53\x9a\x3c\xc5\x8c\x07\xb6\x4d\x4f\xb0\x9c\xc8\xa9\x85\x67\xb7\xf4\x6b\x23\xcd\x52\x77\x19\x14\x2a\xd3\x51\x04\xcb\x3e\x52\xb6\xed\xb8\xd7\xf8\xea\x94\x4e\x98\x80\x25\xc4\xdc\x57\x87\xa8\x4f\xcc\xf3\x3d\xd8\xba\xcc\x00\xa1\x14\xe1\x2a\x9f\xd7\x29\x09\xcb\x2a\x9f\x3f\xc2\xe1\xfa\xd6\x5c\xd5\x20\x6e\xcc\x9e\x77\x97\xb7\x87\xd2\x89\x9b\x66\x28\x9c\xb2\x69\x04\x89\xf6\x50\x17\x2e\x44\x1b\x06\xf7\xa9\xa9\x69\xa4\xea\x95\xdb\xbd\x92\xfb\xec\x10\xf1\xcf\x88\x15\x4a\x44\x94\xed\x86\x0a\x5f\x31\x09\x02\x5c\xd8\xe3\xaa\xa9\x80\xca\x29\x77\xb1\xe0\xfe\xa6\xeb\x4f\x0e\x52\x0b\x28\xab\x1c\x75\x51\xcb\x04\xd7\x36\x21\x23\x6d\x25\xe6\xa8\x9e\xfe\xa9\xef\xa0\xce\x5a\xfb\xbb\xda\x58\x92\xf4\x91\xfb\x31\x7f\x8c\x32\x1d\x5d\x20\xa7\x78\xb3\x95\xfa\xf1\xa3\x52\x9f\xff\x5a\xea\xf3\x87\x52\xdf\xed\xa9\x15\xfb\x1a\x55\x7c\xa8\xab\x40\x46\x37\x90\xa8\x70\x36\xed\xc5\xbe\x6e\x9a\x61\xa9\xc5\x9e\xb4\x4b\x7a\x77\x9d\xbc\x93\xf2\x44\x4b\x79\xba\x25\xe5\xf4\x4c\x6e\xa0\x1a\x48\xfd\x91\xf4\xdd\xdd\x5c\x89\x75\x8d\x15\xab\x39\x29\x36\x56\x92\x28\x27\xbd\x58\xe7\x58\xdb\x6d\xde\x2c\x0f\xdd\x88\x95\x58\x43\x81\x29\xf7\x59\x8e\x76\x0e\x05\x26\x1c\x8a\x8d\xcc\x06\xb9\x6d\x07\xc5\x46\x9c\xb7\xba\xda\x9b\xb9\xa4\x0b\x77\x32\x4c\xbb\x47\x37\xcc\xed\x4c\xd5\xdd\xa5\x40\xee\x69\x82\x05\x64\x98\xd3\xea\x6e\x90\x05\x3c\x47\x96\x4c\x6c\x3b\x9b\x62\x32\xc9\xa6\x56\x4a\x7f\x72\x3e\x3a\x6b\x5c\xa0\x86\x1d\x3c\xeb\xce\x35\x37\x4d\x96\xf4\x21\x57\xce\xc1\xb2\x4a\x0e\x24\x1f\x09\x94\x8a\x57\xfa\x3a\x00\x52\xf3\xdb\x27\xad\xcf\x59\x65\x3d\xf4\x49\x4b\x2c\x34\xd1\xfb\x0c\xaa\x18\xaa\xf4\xbd\x69\x7a\x43\xa4\x77\x57\xff\x30\x9d\x7f\xdb\x03\xa3\xcb\x39\x1b\x2a\x05\x0f\x62\xa8\x87\xb7\x59\x58\x4e\xc2\x73\xdf\xf3\xab\x50\xf6\x5e\x1f\x64\x58\xed\xde\x58\x24\x10\x72\x52\xb5\x5a\x23\xa8\x5a\x77\xaf\x52\xee\x5e\x46\xee\x9e\x4e\x63\x4a\x52\x0b\x95\x0a\xb4\xda\x3e\x0a\xb4\xfa\x5b\x4b\xd3\x2c\xc8\x05\x0a\x89\xb2\xe4\x5b\x0a\xcb\xe3\xa0\xcc\x9c\x2a\x7b\x78\x4c\xfc\x1f\x11\x15\xa6\x2b\x91\x44\xd3\xf4\xf9\xe3\xa7\x9c\x9b\xe6\x47\x56\xc1\xbf\xff\x2d\xac\xde\xd3\xba\x53\x60\xec\xc2\x73\xf0\x9e\xea\xca\xa7\xcc\xff\xca\xa1\xa2\x75\xd5\xa9\x3c\x24\xf9\x1d\x85\xa3\x6e\x75\x2e\xe0\x02\xbc\x67\x5b\xf4\xe4\x51\xd6\xca\xbc\xe1\x09\xc3\x52\xb5\x33\x2d\x2b\x67\xa4\x65\x32\xa5\x64\x4c\x93\xd9\x17\xba\x68\xe6\x82\x66\x94\xbb\xea\x1e\xc8\xf5\x3d\x52\x4a\x99\x3a\xff\xf2\x5b\x3d\x2b\xc4\xfb\x3c\xaf\x88\x01\xbe\x15\xd5\x63\xce\xfa\x03\x3b\x4f\x22\x58\x3a\x25\x45\x7a\xaa\x90\xea\x9d\xb5\x0f\x8b\x96\x5a\x86\xeb\x3c\xd5\xc1\x1e\xb1\x05\xd9\x65\x92\xcc\x64\x4b\xf4\xf4\x38\x32\xd9\xae\x0a\xeb\x69\x80\xea\x8f\xdc\x91\xeb\x27\x51\xa9\x10\x0c\x94\x7d\x55\xa9\x7f\xc2\x8b\x11\xe7\xba\x0a\x60\x8a\xe8\x8d\xdc\x88\x4e\x91\x25\x1c\x58\x57\xc6\x63\xc5\x7c\x67\x8c\xaa\x8a\x31\xd3\x35\x52\xb0\x0d\x20\xd3\x86\x9a\xc5\x96\xc7\x47\x63\x6e\x33\x37\x8c\x9b\x26\xde\x19\xd3\x30\x05\x31\x43\x4d\x4e\x9f\x91\x34\xde\x29\x75\x51\xe6\x39\xdb\xd4\x64\x6f\x2a\x2c\x85\xc1\x2d\x8f\x5b\x31\x07\xd9\x52\x20\xe3\xdc\xef\x9e\x53\xcb\x30\x48\x53\xd3\x79\x28\x43\xa9\xb2\x61\x90\x62\x6c\x2d\x61\x4f\x6d\x3f\x25\x83\x19\xe8\xfa\x57\x09\x64\x69\xf5\xd1\xd6\xda\x01\x7a\xc5\x4a\xa8\x61\x09\x9e\xba\x9c\x63\xb5\x13\xf3\x1e\x8d\x94\x6b\x37\xae\x60\xd2\x89\xf9\x76\xbb\xd2\x89\xd2\x11\x2f\x62\xd3\xb4\xed\x74\x0b\xf9\xd4\xde\x83\x94\x78\xdf\x38\x3c\x3c\x3c\x34\x14\x8f\xb2\xbc\x69\x8c\xfd\xf6\x95\xf3\x9f\x6c\x68\x65\x4d\x33\xb4\xb2\xbe\x10\xd9\x34\x8d\xa7\x06\x62\xd6\x55\x06\xba\xc4\xf4\xec\x23\x93\x20\x1d\x61\xbd\xb3\xc6\x40\x31\x27\x0e\x65\x8b\xbc\xe4\x8e\xf8\xc6\xca\xed\x6a\x85\x61\xae\x66\xd4\x50\xb7\x33\x5c\x0e\x75\xb7\xd7\x6e\x38\xff\x29\xb1\x6e\xe7\x2c\x2d\xdc\x87\x94\xfe\xe4\xe8\xdd\xf6\x81\x4d\xb7\xa4\x07\x5f\x5b\x33\xae\x60\x90\x15\xaf\xd3\xff\xc9\x4f\x6d\xeb\x80\xba\x04\xea\x4a\xa7\x50\x35\x57\x9f\xe3\xa5\x13\xc3\x05\x92\x1d\x3b\xb8\x63\xc7\x78\x97\x39\x3d\x37\xcd\x0b\x9d\x41\x32\xcd\x8b\xad\xcc\xe9\xf0\x92\x0c\xa7\xf6\x00\xce\x4d\x73\xa8\x47\x0c\x2f\x9a\xe6\x82\x7e\xf4\xdb\x79\x5f\x5f\x21\xda\xf8\x5f\x79\x27\xbb\x78\xe9\x94\x40\x90\x23\x5d\x6b\xe1\xea\xfa\x15\x97\xfb\xdb\xf5\x18\x1c\x44\x5b\x92\x56\xb1\x4b\x15\xc9\x58\x15\x13\x3a\x61\xda\x43\x49\x37\xb9\xb3\x05\x5e\xf4\x8f\x8a\xc7\x56\x78\x0e\xe7\x78\x01\x17\xb8\x82\x5c\x99\x15\xe5\xe4\x91\x49\x49\xad\x05\xac\x70\x32\x55\xb6\x6a\xb5\x55\x7e\x94\x17\xec\x1a\xcf\xe0\x0a\x5f\x92\xab\x1a\xd8\x76\x1e\xa2\x1b\x6c\x8a\xe4\xd7\x78\x31\xc9\xa7\x3b\x57\x30\x57\x0f\xa3\xab\xc6\x85\x12\x53\xa8\x31\xb7\xca\xa0\x0e\xf3\x80\xc7\x78\xae\xee\x4d\x76\xae\x60\x89\xe7\x93\x52\x0f\x4a\x70\xbe\x1b\x5b\xcb\xdd\x35\xc4\xb8\xde\x8d\xad\x64\xe7\x6a\xf7\xca\x5a\x4d\xea\xa9\x55\x40\x81\x2c\x1e\x5d\xab\x1b\x82\x84\x46\x73\x6b\xbe\xbb\x84\xd5\xa4\xb6\xed\x29\xc6\x3b\xd7\x01\x8d\xc3\xa2\x63\x87\x22\xb2\x2c\xe9\xaf\x7a\x67\x90\x6c\xdb\x0a\xa4\x66\x8b\xb6\x6c\xed\x1f\xaa\xf6\xc1\xbd\xcb\x41\x8f\x94\xfb\xf3\xed\x52\x39\x7d\x51\xa8\x5c\xa4\x0c\x1f\x2a\xf8\xe7\xbd\x82\x07\x11\x91\x41\xa0\xe5\xfc\x4a\xa3\xb2\xa5\x4b\x1e\x0f\xcb\x3e\xb7\xa1\xd8\x83\xfb\xc9\x43\x1e\x91\x65\xf1\xda\x85\xa9\x41\x83\x54\x95\x77\xff\x37\x60\x63\x57\x03\xeb\xcc\x54\x07\x73\xec\x76\x30\x55\x0d\xdf\xa3\x14\xfb\x25\x4c\xef\x17\x30\x3d\xa5\xc3\x75\x9c\xbb\xe5\x36\x3a\xe5\x3a\x95\x95\x2e\x4d\xcf\xd1\xfa\xcb\xe9\x0b\x79\xa0\xa6\xd7\x87\xb5\x3c\x50\x62\x37\xaa\xab\xe2\x21\x4f\x90\x84\x25\x45\x39\x51\x25\xda\x5d\xfc\x0d\x33\x8c\xa3\xa4\xd7\x5b\x7e\x02\xcb\x4d\xf9\x53\x1b\xe6\x14\x98\x93\x27\x07\x35\x16\xb0\xb4\xb1\xe0\x90\x87\xae\x69\x2e\x43\xb7\xe3\xee\xe5\x4e\xde\x34\x39\x24\x38\x6b\xbf\x89\x60\x2e\x14\x3c\x58\x86\x45\x50\x58\x98\xf3\xc4\xc2\xd2\xea\xfb\x0a\xc8\x79\x50\x87\xaa\x7c\xbe\xed\x50\xcb\x17\x9c\x43\xac\x6a\xea\x0d\xdb\xb0\x12\x7e\x5b\x61\x1a\x25\xd6\x5f\xce\xfd\x12\x27\x8b\x82\x44\xeb\x2f\xe7\x41\x59\x12\x8f\xd2\x4d\x66\x72\xeb\x4b\xa1\x4f\x9f\xe6\x3f\x0d\xab\xb6\x8c\xdb\x4f\x9f\x7e\x33\xc0\x58\x18\x1c\x8c\x27\xa6\xf1\x00\x46\xb7\x02\xf7\x53\xee\x27\x9b\xc2\x5c\x7d\xd8\xed\xd0\x47\xdd\xbe\x7b\x4a\x13\xbf\xc0\x42\xab\xca\x35\x2e\x9c\x18\xe6\xfd\xbd\x3a\xac\xb0\xda\xbc\x5c\x63\x72\xe7\xc6\xbd\x67\x17\xf6\x05\x87\x1e\x94\xd8\x97\x62\x7f\xc1\x25\xb0\x21\xa3\x48\x5e\xe5\x70\x18\xe7\x4d\x53\x3a\x69\xc5\xbe\x29\xe3\xa2\xcb\x23\xc6\x60\xac\x66\xdf\x07\x73\x91\xe5\x2b\x99\xd1\x56\x06\x86\xc5\x96\x91\x71\xaf\x06\xf8\xb1\x12\x60\x81\xc3\xa5\x69\xaa\x84\xcb\x47\x56\x82\x76\xcc\x3c\xee\x2c\x2a\xc1\xbe\xf1\xa8\xf4\x3b\x37\x74\xdd\xc7\xfe\xdb\x65\xe8\xda\x5c\x17\x6c\x4d\x7c\x3a\x77\x04\xf6\x89\xa3\x85\x23\x6c\x0f\xe6\xca\xaa\xe3\xfb\x09\xab\x31\xdf\xb9\xe1\x2f\xdc\xe8\xc6\xaa\xfd\x7a\x4a\x0b\x0b\xda\x4b\xbc\x5a\xb3\x39\x0f\xdd\x88\x82\x85\xb9\xbf\xf2\x4b\xa8\xf1\x07\xfc\x20\x6f\xa3\x27\x45\xcc\x21\xd1\x90\xdc\x20\x45\x32\xf7\x73\x95\x1d\x54\xb2\xa2\x5c\x80\xb4\xb5\x92\xd7\x9c\x83\x37\xa4\x10\x68\xb5\xa6\x08\x89\x57\x78\x0d\xd7\x28\x61\x85\xc9\xdd\x91\x12\x57\x9c\x22\x17\x09\x73\x2c\xdb\x90\x6a\xd3\x37\xe7\x14\xdc\xc8\x4e\xef\x49\x7c\xc5\x44\x17\x4b\x72\xb8\xd6\xab\x27\x1d\xcc\xce\xa4\x13\xc4\xaa\x43\x49\x6e\xa1\x94\x38\x25\xae\x9c\x12\x17\x4e\x09\xf9\x2e\x8e\x21\xc3\x57\x8c\xac\x6b\x0e\x5f\x79\x0b\x77\xc1\x9d\xd9\x65\xc9\xb8\x42\xfd\x15\x4b\xa0\x7a\xac\x97\xbf\xf0\xa2\xc9\x6a\xeb\x0c\xe0\x7a\xeb\x65\xea\x4f\x92\xed\xbe\x6a\xbb\x0f\x7e\x60\xad\xdd\xf9\x2a\xd7\x35\xc2\x0f\x23\xdf\x2d\xc7\xda\x12\x4d\x43\x06\x38\x72\x77\x85\xa3\xf3\x41\x7a\xee\xbb\xfc\x5a\xa5\x15\xd7\xf9\xf5\x2f\xa2\xa1\x55\x57\x4d\x65\x09\xde\xa5\x07\xc8\x41\xe8\x5d\xf5\xf1\x1e\x18\xa2\x55\xf7\xaa\xfe\x67\xd8\x65\x35\x99\xe0\x4d\x53\x84\x17\x14\x03\x8d\xd0\xe5\x4d\xb3\x9e\x15\xa5\x38\x4e\xf3\x59\xc5\x04\x57\x72\x32\x64\x02\x09\x9d\x7b\x37\x0d\xca\x8f\x5d\xe7\xd7\xcc\x92\x20\x78\x97\x61\xf9\x3d\x9a\xb3\xdf\x47\x37\xd6\x98\xfb\x2e\x6c\xa4\xb0\xad\x48\x2d\x76\xc6\xea\x57\x5d\x8b\xb4\x6e\x19\x0c\x2b\x27\x6e\x2b\x45\x33\xd3\xac\xfa\x6c\xa8\x0a\x8c\x36\xaf\x98\x71\x5d\x1e\xbc\x62\xc5\x68\xcc\xa1\x2b\x5a\x0e\x24\x6e\x7c\x3c\xc8\x4c\x53\xa5\x35\xe4\x5d\x30\xf2\x0e\x98\x3b\xd9\xf8\x0a\xbf\x39\x73\x79\xc5\x2a\xce\x21\x53\x56\xf2\x77\xf8\xda\x5b\xc9\xbe\x48\xfc\x9f\x9b\x35\x55\x15\xb7\xff\x2b\x33\x0d\xe3\xfd\xf6\x60\x35\xa7\x3c\x76\xa6\x5d\x7c\x5b\x11\xff\x62\xe5\x88\x60\x2b\x28\x45\xc4\x3c\x92\x14\x6c\x18\xdd\x1d\x99\x01\x6e\x28\x55\x14\x49\x6a\x9d\xbc\xfd\x0c\x8d\xb3\xd9\x99\xe1\x2b\x57\x9c\xe8\xdb\xfb\x07\x2d\x92\xea\x0b\xd3\xf1\xd3\xee\x13\xd3\xe8\x35\x4b\x59\x06\x39\x07\xb7\x11\xe0\xb9\x20\xb9\xff\x5b\x88\x64\x73\x42\x7c\x12\x25\xaa\xcf\xef\x86\xd0\x62\x55\x17\xd1\xf5\x8b\xb6\xcc\x5e\xd4\x59\xdc\x66\x7b\xd4\xf3\x3f\xbf\x0b\xd0\xf7\x0f\x57\xb3\xb4\x16\xe7\x09\x4d\xcf\x7f\xbf\x38\x7f\x24\x13\xae\x53\xdb\x1b\x51\xbb\xdd\xd0\xbf\xab\x3a\x25\x75\x3e\xdb\xd4\x4b\x54\x9b\x58\xd6\x6d\x7a\x6a\x8a\xd0\x6d\x1a\x81\x88\x59\x94\xf9\x99\xed\xdd\xa9\xaf\xd8\x54\x56\x68\x21\xf3\x40\x6e\x8a\x50\x72\xf5\x9d\x8a\x65\x18\x81\x0c\x8b\xd6\x03\xcd\x50\xa8\x6c\xa3\x65\x18\x50\xe1\x8d\xdd\x7f\xcb\x51\xd9\x76\x90\x51\xf4\x67\x65\x3c\xc8\x2d\xcc\x6e\xdb\x42\x90\x3b\x5f\x25\xe6\x77\xbf\x4a\x94\x3c\xe8\xdd\xc0\x7c\xf3\xbd\x9f\xe5\x35\x8d\xc7\x37\x88\xca\xfb\xb9\x41\xe1\xc4\x90\x53\x54\xa4\xbe\x29\x2a\x49\xa7\x3b\xa5\xaa\x9f\xa1\x18\x2f\x73\xc4\x56\x96\xea\x61\xa6\xc3\x34\x87\xca\x89\x29\x30\x37\xcd\x61\xae\x8a\xba\x9a\xa6\xbf\x0d\xab\xa2\x22\x72\x7d\xbb\xf4\x6b\xe5\xb8\x0c\xb1\x87\x51\x6b\x00\x6e\x58\x43\x81\x09\x62\x0a\x43\xd9\x34\xc3\x9c\xf7\x5e\xb1\xeb\x0f\xe5\xdf\x95\x2e\x6b\xb9\x73\xc5\x96\x84\x69\xd7\xae\x8b\x8b\x58\xd2\xa7\x5c\xf8\x0b\x96\xf6\x74\xe2\x51\xe2\x93\x33\xef\x06\x65\x58\x07\xb5\xce\x22\xcb\x49\x3d\x1d\x62\x3e\xa9\xfb\x60\x9e\x5a\x42\x6a\xe8\xa0\xf6\x9f\x49\x63\x1a\xb9\xfe\x66\xb9\x0d\x15\xf3\xbb\xb7\xb7\x4c\xe8\x8f\x7f\x42\x72\xa6\xab\x10\xb7\xaa\x7d\x6a\x62\x8c\xf6\xa3\xbf\x89\x2e\x8e\x1c\xa8\x52\xb8\xa9\x81\x78\xae\xde\x37\xe5\xe7\x3d\x8b\xea\xef\x91\xc4\xd6\xb9\x95\x0f\xbe\xff\x21\xf7\x46\x45\x5b\xb5\x2a\x94\xef\xbf\x77\xa2\xbd\xb6\xdf\x80\x6e\x38\x46\xda\x76\x90\x4f\xe4\x74\x17\xb3\xb6\x1e\x6c\x52\xa0\x3b\xb5\xf0\xbc\x4f\x03\x88\x2e\x30\x26\x42\xf1\xa0\x78\xd1\x4f\x2e\x2c\x8b\xe7\x93\x62\x1a\x56\xea\x6b\x5d\xad\x53\xf2\x49\x61\x79\x24\xce\xfa\x01\x5d\x0e\xfa\xc9\xa2\xae\xe9\xa8\x6a\x5c\x6a\x98\xee\x60\xd5\xeb\xcf\xed\xbb\x80\x7e\x67\xc9\xb6\x7e\x64\x9b\xaa\xa2\x48\x6c\x22\x75\xcb\x70\x0c\x4b\x6c\x5c\x62\xc1\x2d\xe6\x86\x59\x64\x90\xdf\x24\x2c\x83\x5b\xd9\x06\x60\x7a\x87\xc5\x75\xd9\x5a\xd6\xb9\xc5\x86\xeb\x18\x81\x65\x65\xe4\x04\xab\x6f\xd0\x04\x16\x96\xe8\x0b\x0c\xab\x8d\xc8\x5a\x56\x16\x56\x9b\x69\x06\x64\x36\x56\x81\x6d\x6f\x4d\xb5\xb0\xd0\x33\x2b\x65\x33\x36\x75\x65\xfa\x93\xf7\x2d\x9c\x33\xbe\x89\xd1\x36\x98\xc6\x1b\xe6\x18\x08\xbc\x63\x48\x81\x2c\xf4\x9c\x09\xee\xaf\x88\x0f\x68\x33\x33\x1d\xf7\xeb\x6a\x87\x4f\x73\x8b\x7d\x72\x3e\xcd\x77\x79\xd4\xd0\xaf\xc5\x99\x98\x58\xf6\x34\xa2\xc7\xe8\xc9\x88\xdc\x26\x65\x70\x63\x21\x53\x58\xe9\x67\x75\xd5\x0a\xd7\xd8\x56\xeb\x0e\x2e\xf3\x3c\x15\xb3\x6c\x90\x17\x83\x4b\x99\xcd\x8a\x9b\xc1\x9c\xc2\x4d\x03\xae\x50\x7f\x49\x25\xb3\xc5\x60\x95\xcf\x85\x01\x97\xdd\x87\xe9\x03\x62\xd4\xc1\x72\x56\x0e\x56\x79\x21\x06\xd5\x72\x96\x0d\xbc\xa7\x83\x52\x2e\x32\x99\xc8\x78\x96\x55\x1a\x48\x69\xc0\x39\x1a\xae\x37\xde\xdb\x7f\xfa\xec\xe0\xf9\xe1\xec\x32\x9e\x8b\x64\xb1\x94\x5f\xbe\xa6\xab\x2c\x5f\x7f\x2b\xca\xaa\xbe\xba\xfe\x7e\xf3\xe3\xe5\x6f\xaf\x5e\x1f\x1d\xbf\xf9\xd7\xc9\xef\xff\xdf\xe9\xdb\xb3\xf3\x77\xff\xff\xfb\x8b\x0f\x1f\xff\xf8\xf3\xaf\xff\xfa\xef\x27\x9f\x0d\x38\x43\x4f\x78\xfb\x70\x83\xde\x3e\x5c\xdc\x2f\xec\xf5\xe0\x3d\x4e\x3c\x32\x3f\x9e\xeb\x82\x27\xf6\xc0\x13\xfb\xe0\x89\xa7\xe0\x89\x67\xe0\x89\x03\xf0\xc4\x73\xf0\xc4\x21\x78\x82\x06\x09\xcf\xa3\x3f\x63\xfa\xb3\x37\x85\x97\xea\x43\x8e\x23\xf4\xc4\xa1\xfa\xa2\x4a\x55\x51\x1a\xdd\xf1\x6c\x8a\x9d\xe7\x22\x91\x99\x30\x4d\xfd\xeb\xcc\x56\x73\xae\x1f\xd9\x43\x53\x33\xbb\xdd\x7c\xb7\x69\xd4\x99\x1e\x37\xdf\x54\x7f\xab\x0b\x1b\x61\x9a\xfa\xd7\x21\x2f\xab\xa8\xf4\x05\xc0\xdd\x26\x9c\xc1\x70\xc9\xab\xe2\xe6\xe7\x12\x0b\xf1\xad\x96\x85\x60\x6d\x3d\xa8\xc1\x6f\xe3\x59\x15\x2f\xd9\x6b\xfe\xf3\x56\x73\xa0\x70\xfa\x2f\xcb\x70\x76\xdb\x66\x05\xfe\x63\x34\xfa\xcf\x41\x99\xd7\x45\x2c\xde\xce\xd6\x6b\x99\x2d\x3e\xbe\x3f\xc5\x79\x1e\xdf\xf9\xf7\x1a\xce\x6a\xb6\xfe\x8f\xff\x17\x00\x00\xff\xff\x2f\x88\x72\xca\xa2\x43\x00\x00")
-
-func bignumberJsBytes() ([]byte, error) {
- return bindataRead(
- _bignumberJs,
- "bignumber.js",
- )
-}
-
-func bignumberJs() (*asset, error) {
- bytes, err := bignumberJsBytes()
- if err != nil {
- return nil, err
- }
-
- info := bindataFileInfo{name: "bignumber.js", size: 0, mode: os.FileMode(0), modTime: time.Unix(0, 0)}
- a := &asset{bytes: bytes, info: info}
- return a, nil
-}
-
-// Asset loads and returns the asset for the given name.
-// It returns an error if the asset could not be found or
-// could not be loaded.
-func Asset(name string) ([]byte, error) {
- cannonicalName := strings.Replace(name, "\\", "/", -1)
- 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 a.bytes, nil
- }
- return nil, fmt.Errorf("Asset %s not found", name)
-}
-
-// MustAsset is like Asset but panics when Asset would return an error.
-// It simplifies safe initialization of global variables.
-func MustAsset(name string) []byte {
- a, err := Asset(name)
- if err != nil {
- panic("asset: Asset(" + name + "): " + err.Error())
- }
-
- return a
-}
-
-// 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) {
- cannonicalName := strings.Replace(name, "\\", "/", -1)
- 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 a.info, nil
- }
- return nil, fmt.Errorf("AssetInfo %s not found", name)
-}
-
-// AssetNames returns the names of the assets.
-func AssetNames() []string {
- names := make([]string, 0, len(_bindata))
- for name := range _bindata {
- names = append(names, name)
- }
- return names
-}
-
-// _bindata is a table, holding each asset generator, mapped to its name.
-var _bindata = map[string]func() (*asset, error){
- "bignumber.js": bignumberJs,
-}
-
-// 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
-// following hierarchy:
-// data/
-// foo.txt
-// img/
-// 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
-// AssetDir("") will return []string{"data"}.
-func AssetDir(name string) ([]string, error) {
- node := _bintree
- if len(name) != 0 {
- cannonicalName := strings.Replace(name, "\\", "/", -1)
- pathList := strings.Split(cannonicalName, "/")
- for _, p := range pathList {
- node = node.Children[p]
- if node == nil {
- return nil, fmt.Errorf("Asset %s not found", name)
- }
- }
- }
- if node.Func != nil {
- return nil, fmt.Errorf("Asset %s not found", name)
- }
- rv := make([]string, 0, len(node.Children))
- for childName := range node.Children {
- rv = append(rv, childName)
- }
- return rv, nil
-}
-
-type bintree struct {
- Func func() (*asset, error)
- Children map[string]*bintree
-}
-
-var _bintree = &bintree{nil, map[string]*bintree{
- "bignumber.js": {bignumberJs, map[string]*bintree{}},
-}}
-
-// RestoreAsset restores an asset under the given directory
-func RestoreAsset(dir, name string) error {
- data, err := Asset(name)
- if err != nil {
- return err
- }
- info, err := AssetInfo(name)
- if err != nil {
- return err
- }
- err = os.MkdirAll(_filePath(dir, filepath.Dir(name)), os.FileMode(0755))
- if err != nil {
- return err
- }
- err = ioutil.WriteFile(_filePath(dir, name), data, info.Mode())
- if err != nil {
- return err
- }
- 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
-func RestoreAssets(dir, name string) error {
- children, err := AssetDir(name)
- // File
- if err != nil {
- return RestoreAsset(dir, name)
- }
- // Dir
- for _, child := range children {
- err = RestoreAssets(dir, filepath.Join(name, child))
- if err != nil {
- return err
- }
- }
- return nil
-}
-
-func _filePath(dir, name string) string {
- cannonicalName := strings.Replace(name, "\\", "/", -1)
- return filepath.Join(append([]string{dir}, strings.Split(cannonicalName, "/")...)...)
-}
diff --git a/signer/rules/rules.go b/signer/rules/rules.go
index 03e5136730..f37209f645 100644
--- a/signer/rules/rules.go
+++ b/signer/rules/rules.go
@@ -24,9 +24,9 @@ import (
"github.com/dop251/goja"
"github.com/ethereum/go-ethereum/internal/ethapi"
+ "github.com/ethereum/go-ethereum/internal/jsre/deps"
"github.com/ethereum/go-ethereum/log"
"github.com/ethereum/go-ethereum/signer/core"
- "github.com/ethereum/go-ethereum/signer/rules/deps"
"github.com/ethereum/go-ethereum/signer/storage"
)
diff --git a/tests/difficulty_test.go b/tests/difficulty_test.go
index acbf96e712..192dff12cc 100644
--- a/tests/difficulty_test.go
+++ b/tests/difficulty_test.go
@@ -76,6 +76,9 @@ func TestDifficulty(t *testing.T) {
dt.config("EIP2384", params.ChainConfig{
MuirGlacierBlock: big.NewInt(0),
})
+ dt.config("EIP4345", params.ChainConfig{
+ ArrowGlacierBlock: big.NewInt(0),
+ })
dt.config("difficulty.json", mainnetChainConfig)
dt.walk(t, difficultyTestDir, func(t *testing.T, name string, test *DifficultyTest) {
diff --git a/tests/fuzzers/bls12381/bls12381_fuzz.go b/tests/fuzzers/bls12381/bls12381_fuzz.go
index c0f452f3ed..b283ed11fe 100644
--- a/tests/fuzzers/bls12381/bls12381_fuzz.go
+++ b/tests/fuzzers/bls12381/bls12381_fuzz.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 gofuzz
// +build gofuzz
package bls
diff --git a/tests/fuzzers/stacktrie/trie_fuzzer.go b/tests/fuzzers/stacktrie/trie_fuzzer.go
index 5cea7769c2..e73ef4851a 100644
--- a/tests/fuzzers/stacktrie/trie_fuzzer.go
+++ b/tests/fuzzers/stacktrie/trie_fuzzer.go
@@ -173,7 +173,7 @@ func (f *fuzzer) fuzz() int {
return 0
}
// Flush trie -> database
- rootA, err := trieA.Commit(nil)
+ rootA, _, err := trieA.Commit(nil)
if err != nil {
panic(err)
}
diff --git a/tests/fuzzers/trie/trie-fuzzer.go b/tests/fuzzers/trie/trie-fuzzer.go
index d0b614c9ac..e993af47cf 100644
--- a/tests/fuzzers/trie/trie-fuzzer.go
+++ b/tests/fuzzers/trie/trie-fuzzer.go
@@ -162,11 +162,11 @@ func runRandTest(rt randTest) error {
rt[i].err = fmt.Errorf("mismatch for key 0x%x, got 0x%x want 0x%x", step.key, v, want)
}
case opCommit:
- _, rt[i].err = tr.Commit(nil)
+ _, _, rt[i].err = tr.Commit(nil)
case opHash:
tr.Hash()
case opReset:
- hash, err := tr.Commit(nil)
+ hash, _, err := tr.Commit(nil)
if err != nil {
return err
}
diff --git a/tests/init.go b/tests/init.go
index b0a38e68b0..d6b5b3043d 100644
--- a/tests/init.go
+++ b/tests/init.go
@@ -151,6 +151,7 @@ var Forks = map[string]*params.ChainConfig{
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
+ MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
},
"BerlinToLondonAt5": {
@@ -163,6 +164,7 @@ var Forks = map[string]*params.ChainConfig{
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
+ MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(5),
},
@@ -176,10 +178,11 @@ var Forks = map[string]*params.ChainConfig{
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
+ MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
},
- "Aleut": {
+ "ArrowGlacier": {
ChainID: big.NewInt(1),
HomesteadBlock: big.NewInt(0),
EIP150Block: big.NewInt(0),
@@ -189,8 +192,10 @@ var Forks = map[string]*params.ChainConfig{
ConstantinopleBlock: big.NewInt(0),
PetersburgBlock: big.NewInt(0),
IstanbulBlock: big.NewInt(0),
+ MuirGlacierBlock: big.NewInt(0),
BerlinBlock: big.NewInt(0),
LondonBlock: big.NewInt(0),
+ ArrowGlacierBlock: big.NewInt(0),
},
}
diff --git a/tests/state_test.go b/tests/state_test.go
index c2ca0e8d69..9554e75630 100644
--- a/tests/state_test.go
+++ b/tests/state_test.go
@@ -42,6 +42,7 @@ func TestState(t *testing.T) {
// Very time consuming
st.skipLoad(`^stTimeConsuming/`)
+ st.skipLoad(`.*vmPerformance/loop.*`)
// Uses 1GB RAM per tested fork
st.skipLoad(`^stStaticCall/static_Call1MB`)
@@ -114,7 +115,7 @@ func withTrace(t *testing.T, gasLimit uint64, test func(vm.Config) error) {
}
buf := new(bytes.Buffer)
w := bufio.NewWriter(buf)
- tracer := vm.NewJSONLogger(&vm.LogConfig{DisableMemory: true}, w)
+ tracer := vm.NewJSONLogger(&vm.LogConfig{}, w)
config.Debug, config.Tracer = true, tracer
err2 := test(config)
if !reflect.DeepEqual(err, err2) {
diff --git a/trie/committer.go b/trie/committer.go
index ce4065f5fd..0721990a21 100644
--- a/trie/committer.go
+++ b/trie/committer.go
@@ -72,24 +72,24 @@ func returnCommitterToPool(h *committer) {
committerPool.Put(h)
}
-// commit collapses a node down into a hash node and inserts it into the database
-func (c *committer) Commit(n node, db *Database) (hashNode, error) {
+// Commit collapses a node down into a hash node and inserts it into the database
+func (c *committer) Commit(n node, db *Database) (hashNode, int, error) {
if db == nil {
- return nil, errors.New("no db provided")
+ return nil, 0, errors.New("no db provided")
}
- h, err := c.commit(n, db)
+ h, committed, err := c.commit(n, db)
if err != nil {
- return nil, err
+ return nil, 0, err
}
- return h.(hashNode), nil
+ return h.(hashNode), committed, nil
}
// commit collapses a node down into a hash node and inserts it into the database
-func (c *committer) commit(n node, db *Database) (node, error) {
+func (c *committer) commit(n node, db *Database) (node, int, error) {
// if this path is clean, use available cached data
hash, dirty := n.cache()
if hash != nil && !dirty {
- return hash, nil
+ return hash, 0, nil
}
// Commit children, then parent, and remove remove the dirty flag.
switch cn := n.(type) {
@@ -97,37 +97,38 @@ func (c *committer) commit(n node, db *Database) (node, error) {
// Commit child
collapsed := cn.copy()
- // If the child is fullnode, recursively commit.
- // Otherwise it can only be hashNode or valueNode.
+ // If the child is fullNode, recursively commit,
+ // otherwise it can only be hashNode or valueNode.
+ var childCommitted int
if _, ok := cn.Val.(*fullNode); ok {
- childV, err := c.commit(cn.Val, db)
+ childV, committed, err := c.commit(cn.Val, db)
if err != nil {
- return nil, err
+ return nil, 0, err
}
- collapsed.Val = childV
+ collapsed.Val, childCommitted = childV, committed
}
// The key needs to be copied, since we're delivering it to database
collapsed.Key = hexToCompact(cn.Key)
hashedNode := c.store(collapsed, db)
if hn, ok := hashedNode.(hashNode); ok {
- return hn, nil
+ return hn, childCommitted + 1, nil
}
- return collapsed, nil
+ return collapsed, childCommitted, nil
case *fullNode:
- hashedKids, err := c.commitChildren(cn, db)
+ hashedKids, childCommitted, err := c.commitChildren(cn, db)
if err != nil {
- return nil, err
+ return nil, 0, err
}
collapsed := cn.copy()
collapsed.Children = hashedKids
hashedNode := c.store(collapsed, db)
if hn, ok := hashedNode.(hashNode); ok {
- return hn, nil
+ return hn, childCommitted + 1, nil
}
- return collapsed, nil
+ return collapsed, childCommitted, nil
case hashNode:
- return cn, nil
+ return cn, 0, nil
default:
// nil, valuenode shouldn't be committed
panic(fmt.Sprintf("%T: invalid node: %v", n, n))
@@ -135,8 +136,11 @@ func (c *committer) commit(n node, db *Database) (node, error) {
}
// commitChildren commits the children of the given fullnode
-func (c *committer) commitChildren(n *fullNode, db *Database) ([17]node, error) {
- var children [17]node
+func (c *committer) commitChildren(n *fullNode, db *Database) ([17]node, int, error) {
+ var (
+ committed int
+ children [17]node
+ )
for i := 0; i < 16; i++ {
child := n.Children[i]
if child == nil {
@@ -144,25 +148,26 @@ func (c *committer) commitChildren(n *fullNode, db *Database) ([17]node, error)
}
// If it's the hashed child, save the hash value directly.
// Note: it's impossible that the child in range [0, 15]
- // is a valuenode.
+ // is a valueNode.
if hn, ok := child.(hashNode); ok {
children[i] = hn
continue
}
// Commit the child recursively and store the "hashed" value.
// Note the returned node can be some embedded nodes, so it's
- // possible the type is not hashnode.
- hashed, err := c.commit(child, db)
+ // possible the type is not hashNode.
+ hashed, childCommitted, err := c.commit(child, db)
if err != nil {
- return children, err
+ return children, 0, err
}
children[i] = hashed
+ committed += childCommitted
}
// For the 17th child, it's possible the type is valuenode.
if n.Children[16] != nil {
children[16] = n.Children[16]
}
- return children, nil
+ return children, committed, nil
}
// store hashes the node n and if we have a storage layer specified, it writes
@@ -176,7 +181,7 @@ func (c *committer) store(n node, db *Database) node {
)
if hash == nil {
// This was not generated - must be a small node stored in the parent.
- // In theory we should apply the leafCall here if it's not nil(embedded
+ // In theory, we should apply the leafCall here if it's not nil(embedded
// node usually contains value). But small value(less than 32bytes) is
// not our target.
return n
@@ -224,7 +229,7 @@ func (c *committer) commitLoop(db *Database) {
}
case *fullNode:
// For children in range [0, 15], it's impossible
- // to contain valuenode. Only check the 17th child.
+ // to contain valueNode. Only check the 17th child.
if n.Children[16] != nil {
c.onleaf(nil, nil, n.Children[16].(valueNode), hash)
}
diff --git a/trie/database.go b/trie/database.go
index f140a56642..58ca4e6f3c 100644
--- a/trie/database.go
+++ b/trie/database.go
@@ -794,8 +794,8 @@ type cleaner struct {
// Put reacts to database writes and implements dirty data uncaching. This is the
// post-processing step of a commit operation where the already persisted trie is
// removed from the dirty cache and moved into the clean cache. The reason behind
-// the two-phase commit is to ensure ensure data availability while moving from
-// memory to disk.
+// the two-phase commit is to ensure data availability while moving from memory
+// to disk.
func (c *cleaner) Put(key []byte, rlp []byte) error {
hash := common.BytesToHash(key)
diff --git a/trie/iterator_test.go b/trie/iterator_test.go
index 2518f7bac8..95cafdd3bd 100644
--- a/trie/iterator_test.go
+++ b/trie/iterator_test.go
@@ -393,7 +393,7 @@ func testIteratorContinueAfterSeekError(t *testing.T, memonly bool) {
for _, val := range testdata1 {
ctr.Update([]byte(val.k), []byte(val.v))
}
- root, _ := ctr.Commit(nil)
+ root, _, _ := ctr.Commit(nil)
if !memonly {
triedb.Commit(root, true, nil)
}
diff --git a/trie/secure_trie.go b/trie/secure_trie.go
index e38471c1b7..18be12d34a 100644
--- a/trie/secure_trie.go
+++ b/trie/secure_trie.go
@@ -20,7 +20,9 @@ import (
"fmt"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
)
// SecureTrie wraps a trie with key hashing. In a secure trie, all
@@ -85,6 +87,21 @@ func (t *SecureTrie) TryGetNode(path []byte) ([]byte, int, error) {
return t.trie.TryGetNode(path)
}
+// TryUpdate account will abstract the write of an account to the
+// secure trie.
+func (t *SecureTrie) TryUpdateAccount(key []byte, acc *types.StateAccount) error {
+ hk := t.hashKey(key)
+ data, err := rlp.EncodeToBytes(acc)
+ if err != nil {
+ return err
+ }
+ if err := t.trie.TryUpdate(hk, data); err != nil {
+ return err
+ }
+ t.getSecKeyCache()[string(hk)] = common.CopyBytes(key)
+ return nil
+}
+
// Update associates key with value in the trie. Subsequent calls to
// Get will return value. If value has length zero, any existing value
// is deleted from the trie and calls to Get will return nil.
@@ -144,7 +161,7 @@ func (t *SecureTrie) GetKey(shaKey []byte) []byte {
//
// Committing flushes nodes from memory. Subsequent Get calls will load nodes
// from the database.
-func (t *SecureTrie) Commit(onleaf LeafCallback) (root common.Hash, err error) {
+func (t *SecureTrie) Commit(onleaf LeafCallback) (common.Hash, int, error) {
// Write all the pre-images to the actual disk database
if len(t.getSecKeyCache()) > 0 {
if t.trie.db.preimages != nil { // Ugly direct check but avoids the below write lock
diff --git a/trie/stacktrie_test.go b/trie/stacktrie_test.go
index ccdf389d52..bd2574d5d1 100644
--- a/trie/stacktrie_test.go
+++ b/trie/stacktrie_test.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 trie
import (
diff --git a/trie/trie.go b/trie/trie.go
index e492a532cb..13343112b8 100644
--- a/trie/trie.go
+++ b/trie/trie.go
@@ -24,8 +24,10 @@ import (
"sync"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/log"
+ "github.com/ethereum/go-ethereum/rlp"
)
var (
@@ -174,6 +176,10 @@ func (t *Trie) TryGetNode(path []byte) ([]byte, int, error) {
}
func (t *Trie) tryGetNode(origNode node, path []byte, pos int) (item []byte, newnode node, resolved int, err error) {
+ // If non-existent path requested, abort
+ if origNode == nil {
+ return nil, nil, 0, nil
+ }
// If we reached the requested path, return the current node
if pos >= len(path) {
// Although we most probably have the original node expanded, encoding
@@ -193,10 +199,6 @@ func (t *Trie) tryGetNode(origNode node, path []byte, pos int) (item []byte, new
}
// Path still needs to be traversed, descend into children
switch n := (origNode).(type) {
- case nil:
- // Non-existent path requested, abort
- return nil, nil, 0, nil
-
case valueNode:
// Path prematurely ended, abort
return nil, nil, 0, nil
@@ -246,6 +248,14 @@ func (t *Trie) Update(key, value []byte) {
}
}
+func (t *Trie) TryUpdateAccount(key []byte, acc *types.StateAccount) error {
+ data, err := rlp.EncodeToBytes(acc)
+ if err != nil {
+ return fmt.Errorf("can't encode object at %x: %w", key[:], err)
+ }
+ return t.TryUpdate(key, data)
+}
+
// TryUpdate associates key with value in the trie. Subsequent calls to
// Get will return value. If value has length zero, any existing value
// is deleted from the trie and calls to Get will return nil.
@@ -514,12 +524,12 @@ func (t *Trie) Hash() common.Hash {
// Commit writes all nodes to the trie's memory database, tracking the internal
// and external (for account tries) references.
-func (t *Trie) Commit(onleaf LeafCallback) (root common.Hash, err error) {
+func (t *Trie) Commit(onleaf LeafCallback) (common.Hash, int, error) {
if t.db == nil {
panic("commit called on trie with nil database")
}
if t.root == nil {
- return emptyRoot, nil
+ return emptyRoot, 0, nil
}
// Derive the hash for all dirty nodes first. We hold the assumption
// in the following procedure that all nodes are hashed.
@@ -531,7 +541,7 @@ func (t *Trie) Commit(onleaf LeafCallback) (root common.Hash, err error) {
// up goroutines. This can happen e.g. if we load a trie for reading storage
// values, but don't write to it.
if _, dirty := t.root.cache(); !dirty {
- return rootHash, nil
+ return rootHash, 0, nil
}
var wg sync.WaitGroup
if onleaf != nil {
@@ -543,8 +553,7 @@ func (t *Trie) Commit(onleaf LeafCallback) (root common.Hash, err error) {
h.commitLoop(t.db)
}()
}
- var newRoot hashNode
- newRoot, err = h.Commit(t.root, t.db)
+ newRoot, committed, err := h.Commit(t.root, t.db)
if onleaf != nil {
// The leafch is created in newCommitter if there was an onleaf callback
// provided. The commitLoop only _reads_ from it, and the commit
@@ -554,10 +563,10 @@ func (t *Trie) Commit(onleaf LeafCallback) (root common.Hash, err error) {
wg.Wait()
}
if err != nil {
- return common.Hash{}, err
+ return common.Hash{}, 0, err
}
t.root = newRoot
- return rootHash, nil
+ return rootHash, committed, nil
}
// hashRoot calculates the root hash of the given trie
diff --git a/trie/trie_test.go b/trie/trie_test.go
index 492b423c2f..be0df8a544 100644
--- a/trie/trie_test.go
+++ b/trie/trie_test.go
@@ -32,6 +32,7 @@ import (
"github.com/davecgh/go-spew/spew"
"github.com/ethereum/go-ethereum/common"
+ "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/crypto"
"github.com/ethereum/go-ethereum/ethdb"
"github.com/ethereum/go-ethereum/ethdb/leveldb"
@@ -90,7 +91,7 @@ func testMissingNode(t *testing.T, memonly bool) {
trie, _ := New(common.Hash{}, triedb)
updateString(trie, "120000", "qwerqwerqwerqwerqwerqwerqwerqwer")
updateString(trie, "123456", "asdfasdfasdfasdfasdfasdfasdfasdf")
- root, _ := trie.Commit(nil)
+ root, _, _ := trie.Commit(nil)
if !memonly {
triedb.Commit(root, true, nil)
}
@@ -172,7 +173,7 @@ func TestInsert(t *testing.T) {
updateString(trie, "A", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")
exp = common.HexToHash("d23786fb4a010da3ce639d66d5e904a11dbc02746d1ce25029e53290cabf28ab")
- root, err := trie.Commit(nil)
+ root, _, err := trie.Commit(nil)
if err != nil {
t.Fatalf("commit error: %v", err)
}
@@ -270,7 +271,7 @@ func TestReplication(t *testing.T) {
for _, val := range vals {
updateString(trie, val.k, val.v)
}
- exp, err := trie.Commit(nil)
+ exp, _, err := trie.Commit(nil)
if err != nil {
t.Fatalf("commit error: %v", err)
}
@@ -285,7 +286,7 @@ func TestReplication(t *testing.T) {
t.Errorf("trie2 doesn't have %q => %q", kv.k, kv.v)
}
}
- hash, err := trie2.Commit(nil)
+ hash, _, err := trie2.Commit(nil)
if err != nil {
t.Fatalf("commit error: %v", err)
}
@@ -429,11 +430,11 @@ func runRandTest(rt randTest) bool {
rt[i].err = fmt.Errorf("mismatch for key 0x%x, got 0x%x want 0x%x", step.key, v, want)
}
case opCommit:
- _, rt[i].err = tr.Commit(nil)
+ _, _, rt[i].err = tr.Commit(nil)
case opHash:
tr.Hash()
case opReset:
- hash, err := tr.Commit(nil)
+ hash, _, err := tr.Commit(nil)
if err != nil {
rt[i].err = err
return false
@@ -553,13 +554,6 @@ func BenchmarkHash(b *testing.B) {
trie.Hash()
}
-type account struct {
- Nonce uint64
- Balance *big.Int
- Root common.Hash
- Code []byte
-}
-
// Benchmarks the trie Commit following a Hash. Since the trie caches the result of any operation,
// we cannot use b.N as the number of hashing rouns, since all rounds apart from
// the first one will be NOOP. As such, we'll use b.N as the number of account to
@@ -568,7 +562,7 @@ func BenchmarkCommitAfterHash(b *testing.B) {
b.Run("no-onleaf", func(b *testing.B) {
benchmarkCommitAfterHash(b, nil)
})
- var a account
+ var a types.StateAccount
onleaf := func(paths [][]byte, hexpath []byte, leaf []byte, parent common.Hash) error {
rlp.DecodeBytes(leaf, &a)
return nil
@@ -633,7 +627,7 @@ func TestCommitAfterHash(t *testing.T) {
if exp != root {
t.Errorf("got %x, exp %x", root, exp)
}
- root, _ = trie.Commit(nil)
+ root, _, _ = trie.Commit(nil)
if exp != root {
t.Errorf("got %x, exp %x", root, exp)
}
@@ -664,7 +658,7 @@ func makeAccounts(size int) (addresses [][20]byte, accounts [][]byte) {
balanceBytes := make([]byte, numBytes)
random.Read(balanceBytes)
balance := new(big.Int).SetBytes(balanceBytes)
- data, _ := rlp.EncodeToBytes(&account{nonce, balance, root, code})
+ data, _ := rlp.EncodeToBytes(&types.StateAccount{Nonce: nonce, Balance: balance, Root: root, CodeHash: code})
accounts[i] = data
}
return addresses, accounts
@@ -740,7 +734,7 @@ func TestCommitSequence(t *testing.T) {
trie.Update(crypto.Keccak256(addresses[i][:]), accounts[i])
}
// Flush trie -> database
- root, _ := trie.Commit(nil)
+ root, _, _ := trie.Commit(nil)
// Flush memdb -> disk (sponge)
db.Commit(root, false, func(c common.Hash) {
// And spongify the callback-order
@@ -792,7 +786,7 @@ func TestCommitSequenceRandomBlobs(t *testing.T) {
trie.Update(key, val)
}
// Flush trie -> database
- root, _ := trie.Commit(nil)
+ root, _, _ := trie.Commit(nil)
// Flush memdb -> disk (sponge)
db.Commit(root, false, func(c common.Hash) {
// And spongify the callback-order
@@ -834,7 +828,7 @@ func TestCommitSequenceStackTrie(t *testing.T) {
stTrie.TryUpdate(key, val)
}
// Flush trie -> database
- root, _ := trie.Commit(nil)
+ root, _, _ := trie.Commit(nil)
// Flush memdb -> disk (sponge)
db.Commit(root, false, nil)
// And flush stacktrie -> disk
@@ -879,7 +873,7 @@ func TestCommitSequenceSmallRoot(t *testing.T) {
trie.TryUpdate(key, []byte{0x1})
stTrie.TryUpdate(key, []byte{0x1})
// Flush trie -> database
- root, _ := trie.Commit(nil)
+ root, _, _ := trie.Commit(nil)
// Flush memdb -> disk (sponge)
db.Commit(root, false, nil)
// And flush stacktrie -> disk