From a6610ba08207d83c093bf1b5c42011e80eccc0d7 Mon Sep 17 00:00:00 2001 From: Roman Khimov Date: Tue, 15 Oct 2019 12:52:10 +0300 Subject: [PATCH] core: verify blocks, fix #12 This adds the following verifications: * merkleroot check * index check * timestamp check * witnesses verification VerifyWitnesses is also renamed to verifyTxWitnesses here to not confuse it with verifyBlockWitnesse and to hide it from external access (no users at the moment). --- pkg/core/block.go | 26 +++--- pkg/core/block_base.go | 35 ++++++-- pkg/core/block_test.go | 48 +++++++++-- pkg/core/blockchain.go | 136 +++++++++++++++++++----------- pkg/core/blockchain_test.go | 19 +---- pkg/core/helper_test.go | 86 +++++++++++++++---- pkg/rpc/server_helper_test.go | 63 ++++---------- pkg/rpc/testdata/50testblocks.acc | Bin 0 -> 25954 bytes 8 files changed, 260 insertions(+), 153 deletions(-) create mode 100644 pkg/rpc/testdata/50testblocks.acc diff --git a/pkg/core/block.go b/pkg/core/block.go index 23bc73c40..d15c4beee 100644 --- a/pkg/core/block.go +++ b/pkg/core/block.go @@ -9,7 +9,6 @@ import ( "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/util" "github.com/Workiva/go-datastructures/queue" - log "github.com/sirupsen/logrus" ) // Block represents one block in the chain. @@ -31,14 +30,18 @@ func (b *Block) Header() *Header { } } -// rebuildMerkleRoot rebuild the merkleroot of the block. -func (b *Block) rebuildMerkleRoot() error { - hashes := make([]util.Uint256, len(b.Transactions)) - for i, tx := range b.Transactions { +func merkleTreeFromTransactions(txes []*transaction.Transaction) (*crypto.MerkleTree, error) { + hashes := make([]util.Uint256, len(txes)) + for i, tx := range txes { hashes[i] = tx.Hash() } - merkle, err := crypto.NewMerkleTree(hashes) + return crypto.NewMerkleTree(hashes) +} + +// rebuildMerkleRoot rebuild the merkleroot of the block. +func (b *Block) rebuildMerkleRoot() error { + merkle, err := merkleTreeFromTransactions(b.Transactions) if err != nil { return err } @@ -48,7 +51,7 @@ func (b *Block) rebuildMerkleRoot() error { } // Verify the integrity of the block. -func (b *Block) Verify(full bool) error { +func (b *Block) Verify() error { // There has to be some transaction inside. if len(b.Transactions) == 0 { return errors.New("no transactions") @@ -63,9 +66,12 @@ func (b *Block) Verify(full bool) error { return fmt.Errorf("miner transaction %s is not the first one", tx.Hash().ReverseString()) } } - // TODO: When full is true, do a full verification. - if full { - log.Warn("full verification of blocks is not yet implemented") + merkle, err := merkleTreeFromTransactions(b.Transactions) + if err != nil { + return err + } + if !b.MerkleRoot.Equals(merkle.Root()) { + return errors.New("MerkleRoot mismatch") } return nil } diff --git a/pkg/core/block_base.go b/pkg/core/block_base.go index 6b77e1b5b..97077b690 100644 --- a/pkg/core/block_base.go +++ b/pkg/core/block_base.go @@ -40,8 +40,11 @@ type BlockBase struct { // Script used to validate the block Script *transaction.Witness `json:"script"` - // hash of this block, created when binary encoded. + // Hash of this block, created when binary encoded (double SHA256). hash util.Uint256 + + // Hash of the block used to verify it (single SHA256). + verificationHash util.Uint256 } // Verify verifies the integrity of the BlockBase. @@ -58,6 +61,16 @@ func (b *BlockBase) Hash() util.Uint256 { return b.hash } +// VerificationHash returns the hash of the block used to verify it. +func (b *BlockBase) VerificationHash() util.Uint256 { + if b.verificationHash.Equals(util.Uint256{}) { + if b.createHash() != nil { + panic("failed to compute hash!") + } + } + return b.verificationHash +} + // DecodeBinary implements Serializable interface. func (b *BlockBase) DecodeBinary(br *io.BinReader) { b.decodeHashableFields(br) @@ -80,6 +93,16 @@ func (b *BlockBase) EncodeBinary(bw *io.BinWriter) { b.Script.EncodeBinary(bw) } +// getHashableData returns serialized hashable data of the block. +func (b *BlockBase) getHashableData() ([]byte, error) { + buf := io.NewBufBinWriter() + b.encodeHashableFields(buf.BinWriter) + if buf.Err != nil { + return nil, buf.Err + } + return buf.Bytes(), nil +} + // createHash creates the hash of the block. // When calculating the hash value of the block, instead of calculating the entire block, // only first seven fields in the block head will be calculated, which are @@ -87,12 +110,12 @@ func (b *BlockBase) EncodeBinary(bw *io.BinWriter) { // Since MerkleRoot already contains the hash value of all transactions, // the modification of transaction will influence the hash value of the block. func (b *BlockBase) createHash() error { - buf := io.NewBufBinWriter() - b.encodeHashableFields(buf.BinWriter) - if buf.Err != nil { - return buf.Err + bb, err := b.getHashableData() + if err != nil { + return err } - b.hash = hash.DoubleSha256(buf.Bytes()) + b.hash = hash.DoubleSha256(bb) + b.verificationHash = hash.Sha256(bb) return nil } diff --git a/pkg/core/block_test.go b/pkg/core/block_test.go index bb7016826..1c2f5e790 100644 --- a/pkg/core/block_test.go +++ b/pkg/core/block_test.go @@ -6,6 +6,7 @@ import ( "github.com/CityOfZion/neo-go/pkg/core/transaction" "github.com/CityOfZion/neo-go/pkg/crypto" + "github.com/CityOfZion/neo-go/pkg/crypto/hash" "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" ) @@ -76,30 +77,59 @@ func TestTrimmedBlock(t *testing.T) { } } +func newDumbBlock() *Block { + return &Block{ + BlockBase: BlockBase{ + Version: 0, + PrevHash: hash.Sha256([]byte("a")), + MerkleRoot: hash.Sha256([]byte("b")), + Timestamp: uint32(100500), + Index: 1, + ConsensusData: 1111, + NextConsensus: hash.Hash160([]byte("a")), + Script: &transaction.Witness{ + VerificationScript: []byte{0x51}, // PUSH1 + InvocationScript: []byte{0x61}, // NOP + }, + }, + Transactions: []*transaction.Transaction{ + {Type: transaction.MinerType}, + {Type: transaction.IssueType}, + }, + } +} + func TestHashBlockEqualsHashHeader(t *testing.T) { - block := newBlock(0) + block := newDumbBlock() + assert.Equal(t, block.Hash(), block.Header().Hash()) } func TestBlockVerify(t *testing.T) { - block := newBlock( - 0, - newMinerTX(), - newIssueTX(), - ) - assert.Nil(t, block.Verify(false)) + block := newDumbBlock() + assert.NotNil(t, block.Verify()) + assert.Nil(t, block.rebuildMerkleRoot()) + assert.Nil(t, block.Verify()) block.Transactions = []*transaction.Transaction{ {Type: transaction.IssueType}, {Type: transaction.MinerType}, } - assert.NotNil(t, block.Verify(false)) + assert.NoError(t, block.rebuildMerkleRoot()) + assert.NotNil(t, block.Verify()) block.Transactions = []*transaction.Transaction{ {Type: transaction.MinerType}, {Type: transaction.MinerType}, } - assert.NotNil(t, block.Verify(false)) + assert.NoError(t, block.rebuildMerkleRoot()) + assert.NotNil(t, block.Verify()) + block.Transactions = []*transaction.Transaction{ + {Type: transaction.MinerType}, + {Type: transaction.IssueType}, + {Type: transaction.IssueType}, + } + assert.NotNil(t, block.Verify()) } func TestBinBlockDecodeEncode(t *testing.T) { diff --git a/pkg/core/blockchain.go b/pkg/core/blockchain.go index d286e425e..8b7145199 100644 --- a/pkg/core/blockchain.go +++ b/pkg/core/blockchain.go @@ -206,7 +206,10 @@ func (bc *Blockchain) AddBlock(block *Block) error { return fmt.Errorf("expected block %d, but passed block %d", expectedHeight, block.Index) } if bc.config.VerifyBlocks { - err := block.Verify(false) + err := block.Verify() + if err == nil { + err = bc.VerifyBlock(block) + } if err != nil { return fmt.Errorf("block %s is invalid: %s", block.Hash().ReverseString(), err) } @@ -840,6 +843,21 @@ func (bc *Blockchain) GetMemPool() MemPool { return bc.memPool } +// VerifyBlock verifies block against its current state. +func (bc *Blockchain) VerifyBlock(block *Block) error { + prevHeader, err := bc.GetHeader(block.PrevHash) + if err != nil { + return errors.Wrap(err, "unable to get previous header") + } + if prevHeader.Index+1 != block.Index { + return errors.New("previous header index doesn't match") + } + if prevHeader.Timestamp >= block.Timestamp { + return errors.New("block is not newer than the previous one") + } + return bc.verifyBlockWitnesses(block, prevHeader) +} + // VerifyTx verifies whether a transaction is bonafide or not. Block parameter // is used for easy interop access and can be omitted for transactions that are // not yet added into any block. @@ -870,7 +888,7 @@ func (bc *Blockchain) VerifyTx(t *transaction.Transaction, block *Block) error { } } - return bc.VerifyWitnesses(t, block) + return bc.verifyTxWitnesses(t, block) } func (bc *Blockchain) verifyInputs(t *transaction.Transaction) bool { @@ -1094,14 +1112,63 @@ func (bc *Blockchain) GetScriptHashesForVerifying(t *transaction.Transaction) ([ } -// VerifyWitnesses verify the scripts (witnesses) that come with a given +// verifyHashAgainstScript verifies given hash against the given witness. +func (bc *Blockchain) verifyHashAgainstScript(hash util.Uint160, witness *transaction.Witness, checkedHash util.Uint256, interopCtx *interopContext) error { + verification := witness.VerificationScript + + if len(verification) == 0 { + bb := new(bytes.Buffer) + err := vm.EmitAppCall(bb, hash, false) + if err != nil { + return err + } + verification = bb.Bytes() + } else { + if h := witness.ScriptHash(); hash != h { + return errors.New("witness hash mismatch") + } + } + + vm := vm.New(vm.ModeMute) + vm.SetCheckedHash(checkedHash.Bytes()) + vm.SetScriptGetter(func(hash util.Uint160) []byte { + cs := bc.GetContractState(hash) + if cs == nil { + return nil + } + return cs.Script + }) + vm.RegisterInteropFuncs(interopCtx.getSystemInteropMap()) + vm.RegisterInteropFuncs(interopCtx.getNeoInteropMap()) + vm.LoadScript(verification) + vm.LoadScript(witness.InvocationScript) + vm.Run() + if vm.HasFailed() { + return errors.Errorf("vm failed to execute the script") + } + resEl := vm.Estack().Pop() + if resEl != nil { + res, err := resEl.TryBool() + if err != nil { + return err + } + if !res { + return errors.Errorf("signature check failed") + } + } else { + return errors.Errorf("no result returned from the script") + } + return nil +} + +// verifyTxWitnesses verify the scripts (witnesses) that come with a given // transaction. It can reorder them by ScriptHash, because that's required to // match a slice of script hashes from the Blockchain. Block parameter // is used for easy interop access and can be omitted for transactions that are // not yet added into any block. // Golang implementation of VerifyWitnesses method in C# (https://github.com/neo-project/neo/blob/master/neo/SmartContract/Helper.cs#L87). // Unfortunately the IVerifiable interface could not be implemented because we can't move the References method in blockchain.go to the transaction.go file -func (bc *Blockchain) VerifyWitnesses(t *transaction.Transaction, block *Block) error { +func (bc *Blockchain) verifyTxWitnesses(t *transaction.Transaction, block *Block) error { hashes, err := bc.GetScriptHashesForVerifying(t) if err != nil { return err @@ -1113,57 +1180,30 @@ func (bc *Blockchain) VerifyWitnesses(t *transaction.Transaction, block *Block) } sort.Slice(hashes, func(i, j int) bool { return hashes[i].Less(hashes[j]) }) sort.Slice(witnesses, func(i, j int) bool { return witnesses[i].ScriptHash().Less(witnesses[j].ScriptHash()) }) + interopCtx := newInteropContext(0, bc, block, t) for i := 0; i < len(hashes); i++ { - verification := witnesses[i].VerificationScript - - if len(verification) == 0 { - bb := new(bytes.Buffer) - err = vm.EmitAppCall(bb, hashes[i], false) - if err != nil { - return err - } - verification = bb.Bytes() - } else { - if h := witnesses[i].ScriptHash(); hashes[i] != h { - return errors.Errorf("hash mismatch for script #%d", i) - } - } - - vm := vm.New(vm.ModeMute) - vm.SetCheckedHash(t.VerificationHash().Bytes()) - vm.SetScriptGetter(func(hash util.Uint160) []byte { - cs := bc.GetContractState(hash) - if cs == nil { - return nil - } - return cs.Script - }) - systemInterop := newInteropContext(0, bc, block, t) - vm.RegisterInteropFuncs(systemInterop.getSystemInteropMap()) - vm.RegisterInteropFuncs(systemInterop.getNeoInteropMap()) - vm.LoadScript(verification) - vm.LoadScript(witnesses[i].InvocationScript) - vm.Run() - if vm.HasFailed() { - return errors.Errorf("vm failed to execute the script") - } - resEl := vm.Estack().Pop() - if resEl != nil { - res, err := resEl.TryBool() - if err != nil { - return err - } - if !res { - return errors.Errorf("signature check failed") - } - } else { - return errors.Errorf("no result returned from the script") + err := bc.verifyHashAgainstScript(hashes[i], witnesses[i], t.VerificationHash(), interopCtx) + if err != nil { + numStr := fmt.Sprintf("witness #%d", i) + return errors.Wrap(err, numStr) } } return nil } +// verifyBlockWitnesses is a block-specific implementation of VerifyWitnesses logic. +func (bc *Blockchain) verifyBlockWitnesses(block *Block, prevHeader *Header) error { + var hash util.Uint160 + if prevHeader == nil && block.PrevHash.Equals(util.Uint256{}) { + hash = block.Script.ScriptHash() + } else { + hash = prevHeader.NextConsensus + } + interopCtx := newInteropContext(0, bc, nil, nil) + return bc.verifyHashAgainstScript(hash, block.Script, block.VerificationHash(), interopCtx) +} + func hashAndIndexToBytes(h util.Uint256, index uint32) []byte { buf := io.NewBufBinWriter() buf.WriteLE(h.BytesReverse()) diff --git a/pkg/core/blockchain_test.go b/pkg/core/blockchain_test.go index 27fc544e5..bf88230e3 100644 --- a/pkg/core/blockchain_test.go +++ b/pkg/core/blockchain_test.go @@ -4,7 +4,6 @@ import ( "context" "testing" - "github.com/CityOfZion/neo-go/config" "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/io" "github.com/stretchr/testify/assert" @@ -137,8 +136,9 @@ func TestGetTransaction(t *testing.T) { block := getDecodedBlock(t, 2) bc := newTestChain(t) - assert.Nil(t, bc.AddBlock(b1)) - assert.Nil(t, bc.AddBlock(block)) + // These are from some kind of different chain, so can't be added via AddBlock(). + assert.Nil(t, bc.storeBlock(b1)) + assert.Nil(t, bc.storeBlock(block)) // Test unpersisted and persisted access for j := 0; j < 2; j++ { @@ -154,16 +154,3 @@ func TestGetTransaction(t *testing.T) { assert.NoError(t, bc.persist(context.Background())) } } - -func newTestChain(t *testing.T) *Blockchain { - cfg, err := config.Load("../../config", config.ModeUnitTestNet) - if err != nil { - t.Fatal(err) - } - chain, err := NewBlockchain(storage.NewMemoryStore(), cfg.ProtocolConfiguration) - if err != nil { - t.Fatal(err) - } - go chain.Run(context.Background()) - return chain -} diff --git a/pkg/core/helper_test.go b/pkg/core/helper_test.go index 205ae8bbc..112057f32 100644 --- a/pkg/core/helper_test.go +++ b/pkg/core/helper_test.go @@ -1,6 +1,7 @@ package core import ( + "context" "encoding/hex" "encoding/json" "fmt" @@ -8,32 +9,90 @@ import ( "testing" "time" + "github.com/CityOfZion/neo-go/config" + "github.com/CityOfZion/neo-go/pkg/core/storage" "github.com/CityOfZion/neo-go/pkg/core/transaction" - "github.com/CityOfZion/neo-go/pkg/crypto/hash" + "github.com/CityOfZion/neo-go/pkg/crypto/keys" "github.com/CityOfZion/neo-go/pkg/io" + "github.com/CityOfZion/neo-go/pkg/smartcontract" "github.com/CityOfZion/neo-go/pkg/util" + "github.com/stretchr/testify/require" ) +var newBlockPrevHash util.Uint256 +var unitTestNetCfg config.Config + +var privNetKeys = []string{ + "KxyjQ8eUa4FHt3Gvioyt1Wz29cTUrE4eTqX3yFSk1YFCsPL8uNsY", + "KzfPUYDC9n2yf4fK5ro4C8KMcdeXtFuEnStycbZgX3GomiUsvX6W", + "KzgWE3u3EDp13XPXXuTKZxeJ3Gi8Bsm8f9ijY3ZsCKKRvZUo1Cdn", + "L2oEXKRAAMiPEZukwR5ho2S6SMeQLhcK9mF71ZnF7GvT8dU4Kkgz", +} + +// newTestChain should be called before newBlock invocation to properly setup +// global state. +func newTestChain(t *testing.T) *Blockchain { + var err error + unitTestNetCfg, err = config.Load("../../config", config.ModeUnitTestNet) + if err != nil { + t.Fatal(err) + } + chain, err := NewBlockchain(storage.NewMemoryStore(), unitTestNetCfg.ProtocolConfiguration) + if err != nil { + t.Fatal(err) + } + go chain.Run(context.Background()) + zeroHash, err := chain.GetHeader(chain.GetHeaderHash(0)) + require.Nil(t, err) + newBlockPrevHash = zeroHash.Hash() + return chain +} + func newBlock(index uint32, txs ...*transaction.Transaction) *Block { + validators, _ := getValidators(unitTestNetCfg.ProtocolConfiguration) + vlen := len(validators) + valScript, _ := smartcontract.CreateMultiSigRedeemScript( + vlen-(vlen-1)/3, + validators, + ) + witness := &transaction.Witness{ + VerificationScript: valScript, + } b := &Block{ BlockBase: BlockBase{ Version: 0, - PrevHash: hash.Sha256([]byte("a")), - MerkleRoot: hash.Sha256([]byte("b")), - Timestamp: uint32(time.Now().UTC().Unix()), + PrevHash: newBlockPrevHash, + Timestamp: uint32(time.Now().UTC().Unix()) + index, Index: index, ConsensusData: 1111, - NextConsensus: util.Uint160{}, - Script: &transaction.Witness{ - VerificationScript: []byte{0x0}, - InvocationScript: []byte{0x1}, - }, + NextConsensus: witness.ScriptHash(), + Script: witness, }, Transactions: txs, } - + _ = b.rebuildMerkleRoot() b.createHash() + newBlockPrevHash = b.Hash() + invScript := make([]byte, 0) + for _, wif := range privNetKeys { + pKey, err := keys.NewPrivateKeyFromWIF(wif) + if err != nil { + panic(err) + } + b, err := b.getHashableData() + if err != nil { + panic(err) + } + sig, err := pKey.Sign(b) + if err != nil || len(sig) != 64 { + panic(err) + } + // 0x40 is PUSHBYTES64 + invScript = append(invScript, 0x40) + invScript = append(invScript, sig...) + } + b.Script.InvocationScript = invScript return b } @@ -52,13 +111,6 @@ func newMinerTX() *transaction.Transaction { } } -func newIssueTX() *transaction.Transaction { - return &transaction.Transaction{ - Type: transaction.IssueType, - Data: &transaction.IssueTX{}, - } -} - func getDecodedBlock(t *testing.T, i int) *Block { data, err := getBlockData(i) if err != nil { diff --git a/pkg/rpc/server_helper_test.go b/pkg/rpc/server_helper_test.go index bbb7f8b34..603564d78 100644 --- a/pkg/rpc/server_helper_test.go +++ b/pkg/rpc/server_helper_test.go @@ -3,18 +3,16 @@ package rpc import ( "context" "net/http" + "os" "testing" - "time" "github.com/CityOfZion/neo-go/config" "github.com/CityOfZion/neo-go/pkg/core" "github.com/CityOfZion/neo-go/pkg/core/storage" - "github.com/CityOfZion/neo-go/pkg/core/transaction" - "github.com/CityOfZion/neo-go/pkg/crypto/hash" + "github.com/CityOfZion/neo-go/pkg/io" "github.com/CityOfZion/neo-go/pkg/network" "github.com/CityOfZion/neo-go/pkg/rpc/result" "github.com/CityOfZion/neo-go/pkg/rpc/wrappers" - "github.com/CityOfZion/neo-go/pkg/util" "github.com/stretchr/testify/require" ) @@ -142,6 +140,8 @@ type GetAccountStateResponse struct { } func initServerWithInMemoryChain(ctx context.Context, t *testing.T) (*core.Blockchain, http.HandlerFunc) { + var nBlocks uint32 + net := config.ModeUnitTestNet configPath := "../../config" cfg, err := config.Load(configPath, net) @@ -152,7 +152,18 @@ func initServerWithInMemoryChain(ctx context.Context, t *testing.T) (*core.Block require.NoError(t, err, "could not create chain") go chain.Run(ctx) - initBlocks(t, chain) + + f, err := os.Open("testdata/50testblocks.acc") + require.Nil(t, err) + br := io.NewBinReaderFromIO(f) + br.ReadLE(&nBlocks) + require.Nil(t, br.Err) + for i := 0; i < int(nBlocks); i++ { + block := &core.Block{} + block.DecodeBinary(br) + require.Nil(t, br.Err) + require.NoError(t, chain.AddBlock(block)) + } serverConfig := network.NewServerConfig(cfg) server := network.NewServer(serverConfig, chain) @@ -161,45 +172,3 @@ func initServerWithInMemoryChain(ctx context.Context, t *testing.T) (*core.Block return chain, handler } - -func initBlocks(t *testing.T, chain *core.Blockchain) { - blocks := makeBlocks(10) - for i := 0; i < len(blocks); i++ { - require.NoError(t, chain.AddBlock(blocks[i])) - } -} - -func makeBlocks(n int) []*core.Block { - blocks := make([]*core.Block, n) - for i := 0; i < n; i++ { - blocks[i] = newBlock(uint32(i+1), newMinerTX()) - } - return blocks -} - -func newMinerTX() *transaction.Transaction { - return &transaction.Transaction{ - Type: transaction.MinerType, - Data: &transaction.MinerTX{}, - } -} - -func newBlock(index uint32, txs ...*transaction.Transaction) *core.Block { - b := &core.Block{ - BlockBase: core.BlockBase{ - Version: 0, - PrevHash: hash.Sha256([]byte("a")), - MerkleRoot: hash.Sha256([]byte("b")), - Timestamp: uint32(time.Now().UTC().Unix()), - Index: index, - ConsensusData: 1111, - NextConsensus: util.Uint160{}, - Script: &transaction.Witness{ - VerificationScript: []byte{0x0}, - InvocationScript: []byte{0x1}, - }, - }, - Transactions: txs, - } - return b -} diff --git a/pkg/rpc/testdata/50testblocks.acc b/pkg/rpc/testdata/50testblocks.acc new file mode 100644 index 0000000000000000000000000000000000000000..91825648d4a58ecebe67c74cddd32342f6c6c091 GIT binary patch literal 25954 zcmd7aQ+rs!76#zhcGB3k(>6P{ZKJVmwLzmsZLG#g!^XC4+l`Hr^PHP=mt1Ckz`EJ* z^Ua=_HNy(>zwt9VWI=^4|3-|xX)#xeB7b7G>&Em}fH>w$$9E1co1aH@Fp!dMK=RhL zx6+sHRrctLD>G~J(Mm34e~s10l3Sk5TpiGtQfd(YtJf?(k$pJ^f?kyVv>9rr?`AJY zm<9R>0Sd%UP(_E4?F4_x3m#uW@;_|BE2_;F(5Zla9N-(OvT|8$3941jGi-2SNcFXq zhA;TZ2{>^a3N3jy-fGXqiZ}oQDcacbkd*p>Vm4erE;W(nx4Ngt->DKM*Ai=x7uGGy zPpPa(wB506>j{;oG%$^la(_*@nP>F#MEPyT{7J6>fsB;cyc3mc-Y9r(yF=F>xVc?d zgeQWi@C>|X2cUVw;Gb2V_MSdMO62J2bXx*6aMSoR^~YDdCzG3mGGK79fK9q}@mPh# zDdbp}m=Z_EhPcE4wn>hO6ic*u|htkRI~0@QP51G_8iNKe|CpV-p7HUYvmZ^o&i zv&Hv&I|=w40`!W}cJ#1o3?oXBLUEsPjk!LVr+$Q7R3PjQ;r37E(*J(5{~N|s#^r@Q z&fU=pS<5A#W#^%UO;ZTN9Ihj^T|pdgh0|YS!27}g?4{nT3<_H-6?k~e%;pJ9VLU%!AZ4evow@A~Rk`nzWC*=%bH$W-c9l5p0<}M~7bt$=fE}RPW{syaw2Z zqE^AZ@Mz~aeM(9Oh&-m1sF#om=dtkx)exE&J7=03Yq@*>etAGnGl%DI;m*!#nQYyh z`t{?o0JVbbE4kPAjcX}r` z_BDt344Ts3p=8`t?aT)QP6qit`e#ozKxLosz-W1pE(|hANp-3#B7Hm}S5Zr+PJoHX z(w!Ev`aQK-P5Zo+Bb%HJLMjFVvHmtK+~Y!fD1?hj_zyv?mcoFIu6* zBhEdl`Vo34YrG&dz{8;>!-lT(*~lVRI*)f(!f2%L{$R6SHKz24gjUG)C&*%t#mAnWt&yVwU@E!i0hK zfeL^*_%Dh=dEd;Ph1fL;T=!BatNKt(lAL&NouD)25;B$e8^UiQ)htL$Ozu-S6VFMU zU>cB2F~h<#l}FT!hqQ6vd+rFcPTI9S%dexW&747yu$Ek7Qx*BCtjW#2F6%G9kQXB1 zz_ko~KW;vJwh=W6?Gj1$`rYMO;%m)qy&A!6^^O^|J7HJ4jX0<{EesF^`s=Zc*s!Tw zrI$DaGvo)#{m7VdA) zkOOU49ow`#?*$}koQZ78v`!m5Qp{l7^v4uBA7%Fh#?_xQe%X#o_hBnCW}|pbfH_P7 z{J)j-nI8T)e(pcoq=BT&T@6d-&3xCTf5ZqiTU8Stw;a|qASqBhvK3N$|JAkU;69g8 z1-q4dCk1vB90B*9$DEz^l52TTeoQ_OjnpQsv~!VR1CB5$pghIfe@AT`jKpaym zWi>@dmPtnS=$m>-OO&xA`s?(hxr}>aA(~-)WM78FZrQVah1lB%m@x94e-eW5;1q7S z#82M2eIRQ_PhO^a$k9!b6>EcQxI->l{#3Khk&WtkkH3R)D4T2ZmmL=`B3@VPN04RH zHsDxe^SSi{ZoAnGfXOW-plxxO|l@XitkMjn^1_ zeqjLh76u}_$yVoi{Gk?=?sFV4Zcqj|B+IETzp)Bk{^CHW*y@pHO^ynkoUl|bI8>ZM zt?g{0`(-S3DN0oSPMD|NYefbSgYtU)K4ECx*E=C}VO3lA7^2V3;kR+b9J!20+y_S< z?Q^CwDPJcXURC+xuMg1RrEjdoYPpZ6hcmioEBvGfdP$#d(56g2R(G;`RBAt6Y}Z2! z^N}xc-F6D8`U(*ASgL4069O{kNf%XyFT84NY_yEu7P6qc{|jnx#oWtJES;Nw(H`L}-5 z(NT%R{iHg@e2J_Dl4r1))}LF1RFn8p%;0QK&F-=i9iG(e+fVps%&C`u^dDouP&4Ee zStK6WkdU_%8`g2n&Iyi`*E?ZQT-bqA-7?UmpZDAa6X%T-_RoMAyYd08t1y=0ObZsc zf^L}0N^o*Meu7_EJbbVa7Q2YuX3T;*-*6z*9s#Jxvauxq@qs9k-^C`amWw-zpdd;3 zi1J@6(UELPoPrA4@U#418yRp!$?T5vcGz4J=MA=NF{_V@{`kp+AyW~M-|R(e-!TIw zWU<=xCk}`|*(Efhv8eN93^^LSFs|<6V=lO&=6?TPV*tD`fO!jpL0j)R%32|1hYVdG z2hZOoTpJ_`Rjk5FT@+XfNQ@qRqv_~dBOLslq1#8W3q+|S=Vm%Vd(6!y!n)dw;eiSk zfT;LsrQhic^QntTs6kQSLMQ{1SbSR|!`T|g@3CnL%gf+n8CWx?8Kmud$_Pp zl)64&FYg~Uk{H^3%y_iDkmBpK?%UTG2)r zVW>~scE~&4Rnh_q222z_LTVqr5b(fW30DT?@mPRE6I!ey%_)akZf~0;d|rA znSk2$k=!#+UZi`|8AjW30~q;GEQ)?C^GTe1S6A8WH$rpbC*M1&00x5eVFuqIY4Z$$ zITf*adpSFh6YP&HefKA@!T?W@Ya!k}|4-DfYufLaLBFg7L=9?wFF+}tf@5`qp|fy~ zF4$5N9jWFB_1Hdp{u%?p7Y1-|VKDVXw}?kl-1EF;^`V4PwkFnhU$DI1Zx&x)nZ+KK z02F`b^Q2fs^%*QYhS0!wz02`0Zp*>}qvSbAX=D3C@ z^*Tq(H=O?>*MY|k;o>7&mOm;4iX4V2BcN&0}kDsP)f>_~KuDR9*`{#BQ~v($wBr-VU@^Uek>nh5`Y>74!8hzqyq zchYR7#j86MKz?LS@khTnq+KuFz50JKXQICJ)Ln2-o~p7S_rj3B;`V=ObN_OBdWf3f zsO;SjO@fSCF5-H;W4=B4I59ov+x3na;OR5pZHRmozcp&c*tjMpO|dEz-Xr_Wsrng@ zY0rsd_caDWFAU({!oar)ey!Fg=!j!}w?GbUn(gEYUDj}r-hX!gUzMEEU)>&2s83oD z`zt!v)}LHZ$$zvsaixnP)p=XkQ5IG- z6l)TjsQ3HMyEw#YmT32}IcVCDu~paL=vD8ggzPpKRQ^QzP&k;is)&L+QL zZMcZ`5xD%hF~Gg3>@l_Z$`vZJG|f-o4JtlQuSt#za~brWWRP6HqsQdLX!=5brqB}RFIPLE2an@{SgdD z@;dhQl~_o%|C09{XY`I4jMt(H%ba2ck;~<>;eB~Px?PQyVL8q%P8(SLBgSfi^BM!; z7X}D#VL%FvHINsZ=-%*Yx~TNF04B`0FH0{Eu_0Xq1XNe3w1VeI^uY*4j2(iK!QObA ziZB09uYpwHm=ZQ(Tgx~O)F9BtHK*c5^}qAU0?SC*<^mK#Y|1^L0dxL24& zGZwtBHd)cfIO{}8t-etxom$see05Mb@CXo9*A%Gu3Q1J386TaayP z&#G@5k?r8=dt!`b**;)ag3#O&IpUoS<@CjTM4p?(3dc=qaBU9#vQ zvTs>$CcR?@mQl;09dzi^RC5~+TOlHH#CNLK_GO1%6%_t* z<_aI;2-pL5K?q;)!EMJ#Ez`7?PkKPi5R@qh^f1_Y4DI%2EzqUa0VItz0YM3|cCy29e%%Tx=(sb;MU{#9)G8UxW621svVFl2LWwUG4{vmcSL zhhQNXrNjX1{6qxf??eW@Ka^pN(K(h;*g0EDAX0Zi5K&AyxKfRs-#)nI^3W6Hb9tkA zD}ef0xqVY`I^nFJziTDoa-_2dQZ~@&@aR;HU9GEa|KR@c(_MD7vwSk2?Yatr%3!Nb zhGn#^qw=KYNcacQj050dAGP!^r~92;gAO(2ceYW3H_4m9Tw z=vL!%QtghOZ=u!M?K)UsiuHUJ6JSz5FD!Qkk!!^4Gi*bl!ytcxzhef59yW#|;<2d$ zIoQExg%JXamtXnmj}(02XeU;>zx(XG#z5?a0rFcIw0UV!hj#8Kb)2c({yL5o$t1jz zpRW^z$&|O`VX}ObQ#UR=AzhzHleQkJiqMk%O$Lu-${pyP7P9UNy~k`X3IsX-RlSD- zJNp?ADQe|ufU$XoD7BM0UT9Mo)Eit+RPp;MWfU}MX2}`i!*2T$mjY&bKb(|j9K2ot z{(58w)i5x9VNZ$tD}+IekskUWuFViDlC3l-8I~|7kCN=$Cfi8#(Pqlpo+=5y=A_tE zv5($ie*El-{(eUV^4x3>O35$a`2!@0KMHQq&Dt;+V)tRV2x~Dl_Sya2Od@wpOdIsx zB%1Y?!r3k^rR;F|pghMg5zOwV%FS~taPshT*vVd`cg$c?s^0w?4zJXaZT+fWsC22R zRE~fdF}tf~&1j3#!dCP(2I4OaP~O6z$;j%PM|ih0X0z`84%tnAY4(x?ie29Q*2B&) z9YxngfzT{h-B-@WK-OHira5N03o(#026vTD!{(uDhoUg<0ILOGWjZTL?ls(wA9K6k zh{}R`F{O0~~^H~>}s zXlhil0$0JtUDv)ue#lnQr_^W6I=ofj6Do5gA2O_j)D3Iv=p#dW?Dcmr227zjj;@oe zr%rO|X;7yVEgi7bED`rmQ@Cb4bBfTYaYrLE$X+bg2GtpTG`Na$_d~Y3lfC%fjTnsM z#wFfyWXX*+!>N8UR-l4nbWD?gvY-8q86a&NwNIAgD3*={fkM!9)hmEwCZRFM#-vCq zrhgsh=Xi~Q#0vw|w=h^J*xpu@KtosaLWe8tP}Y}_j^l;a=htdlE@57hwODDTa$jwtle9wD&JeDeeIgkO5)b0$$n=C8a;9nwG?&rL zrXTH2nf~v@}dde37H9tB{#U5^bd7T_$ z&%T9uI@9W5kE;^Rabb(y5Su&*q}l;IQVDQGhDJza@FGT&9{g4K2A5#+M`v0VYO0;|1}1Z zFAUJ$!a&C}#B3u!xL?JVe8DzpAqXaJn%9b&P!iUnOgSryok*Rcn;0!PifPGB?X=zB z%T|08(D5^Ud*<>YyyXX#;*abK4%}(U*-7C=-r_a_@B=jyzy{|l+pt{xR7~so7;XNQ z5DfzSDT)PsbuxGV-y8y%&ojj^f2g%(j{KoIyU_Z7AH|nuGJodk{gQ|+FPJ#iZT^lK zoC{TEH(lCCusvn+@^h`Js}qtNu3J`B!?uVBg?6zMzQ#cMg#pG}7~CP?44bJW<7HVX zAf?b#o3p*443`^W>{8{_46rMw%MCX3_x`%jUZ=^ zMT<7P?E%1Q^U;K5CixV#Yg(2yed?K?&K3rmRXe##n{dekXXx2g{eR&=nfN}PIT8l1 ziLoouWC3Ldphkh3q^_8kS0hf=DLpJIw2&KKXCryK>3-U2!K;67FvSBoB5g9y+Bhpu zGHyhRXVTfYu+dkh)swb#h3tV}5|rov@=Ik!S34XGIzmrA(z_FnvJx8)*sVsNMo*-9 zU!%(w&G?aAu1WqVFC<}R{~4MjaeW@(|4=G$o&RFUkl7lChp`M{z$C`0 zkDOa5RjjX`&Gv% z9qZ4BXEtLekW7B95(=(gWkK$wNhT%nax8Iop90Q`ru?Xm!5vAxF|ISOtz;UAlYt%` zP)#8b$LtB-HrK{PDA=+f8!k=PD5#QCOs&!ERTKOjEF#68z0PvvYg^OY;P1Bdb;4^h7v ztL;o5K6_aC;J}ksYfqv1pD?HYefT*qLX?T9N@8_tzLGyfDCh3j=ud8r_Bzke|5;BiDZ2Q?7r2DWz>H1NE=|m?U-H(c321{!izi%&|hOhTB8(?$?2TqVAj8nfdld+nT zLU1-q@KiR#{6w>iEY|*TvL>Y`|9i>(X!j!6D%<-+j(Uc{47L8l;+kALud?UQs`z)z zz|b4d)0&pD1P1G(g2vlOAe#)NQCgu2+7VoKAG0K<>@^08FAVVB!k~`oknj2yUPO$} zj7`nbwlYC)Ut=q;JANWTQ?Khoz^>THb*R$xk7CDtKx1#NWM+gr85;)t1&hNKk{#MxJ`6aiY{bxx)aCt>CvWB+;(3N z1&Y|@^s=DY)4+Klk(=ze9* zfc*IT8SEq#ae0lF?_eQdEpcleUq{a(#J!ol{XHf;PRKeTsWV7yxj>k0h+HI~A+T9mkcX-X!^b3ZL*)GOu z%}F5BO10gE%)d|<1McjG*BB_jFu;EcgP2%fn^wwbogDE3H)mQKrzQFA-j=Z(pK@<^}EiSF~ zOLTX*;AYefcazne`Rk72p*@mYk#ot!u@d4N@gQUan5P?jW%raP=ae#@J@w}7|{Er z?JP+n$Q`Pb)JysnQV1iH9@;}spv30$J7&=G9dxKr5`~TU`Y%Dv4a71NaZ+`Pu5v6( za|u59EFafwa?o;_$W050I!=~Dd(Zbpqt5A;dq(-h(UhDd0xzs zG+^c}cHKEhj@?MB-T-)Knr;!^MSn@{J#+a=Z?y~^F#%CNTYaCt3)D~wIc-r(Tc5HU z<4hovMK7NOSM6@VF?+%nr;1X67xV-ym2jGRTqS=(D0*n{#_k-GB3SM~;}#~`9J`K{ zK?GV>KHjVQWL`tXQ*{_^=fVZ_3wrtO5{))7v1<{2du$q?UP_>>`NcV_t^eg50VVcg zq>HgC`$&~ud^*pgpEUFxGeG`FR{$M1P2bHmx&4<O3W~L`hqrlMGf1%@m@a0aE3OTBf#=4>`5FVY7Y0OcVW4X| zGH#3~rg1!+UmkL5jxShPeb9+K=xV&Q!FG;6g@>j}Ayk&51sbmV=)FF4h(xWAE!!7} z*&2=_6Mja*mI^4IsIxL8WKhd?kvyGgs)h6@Nsw&7$!F|}wakRnha1QJW=9c^MEcU% z%kV&jWA;RSOX){Ql~J|3^HBxVKO7ghinj|O9V-iEBiP#$+v?Iu5FZ(n^zInPAkYWf zO+xo(Y#sE(#|NEW7Q4mXXT73`5fv21%egsVbr_eeWg0;N!bd@3E=k<|tL;>J1^JOE zHy28#-3&=FoqTa|ubsX6Q=x{Cvi{6fJWNm#dB)C}fGfTsrZie1d+!Znl0dwK<9$CaR%&UihVa@RHH3sT042a*tpoBdc(WKjpOZyoG zPJ~$+y;87QK8!)%ui5VO7LBU(JF?PUb+t5F0m6P4f5p5AUyEBy5CN}}<+;LUl?l24 zJ21?cDuxg@WI!|XX=wzZO155~cwRuo*#WzUhAH)!&6Y;tdl$2&rGeyC*BLJ9eUIwN zT-KiHU5tw(%2_JZ6bGOdPZ`Sg)Fe};%V}Vb#Q~S-k)euW6?~yLS2-9KcibK}gpLg{ zmUfMvMXT`0uocu`Tg4!w?a>}6LsMrSId})~nxcDZ{&2rsfdRZnPOu@+ zGoT1IBs|hw=EHTUZ5;easMzq}^W?_7VCLEviJK3Iho_x=@x3(PF#~xwx)qEXAj|^kmdP?FiNpMW_ST?uRHX}Zi9@4*8VW^#o5?l%#+V9FCGFYT{b|=8 z^ntqca9VR6YGJ%Jk;a}VU>6muWFc4xXI6*?9JNPfB!zl-_tsYtq%Z{oN>R&s_nVn# z28xFWpo3gg{6ocz{A1p5sIhxf_jlVwS2)Aj?h83)Y~ooIn9{~QVHZ$!DU!Be9-r)Q z*<^>fD#pe-pMr=nc`KQPT7ijG6?~!qvgbUv<4(*hSg%yZ2hqzfT zh9EB4P>Oxm6TK7d@_LPd<_iPTw=kIEh4dy1eCAiU@VG}b*5&-|(PQrTG>FGYU&gKT zQ)k8T+VOI}CwM>G-Zn4RaJeeU2-LHuQrh95y=eK@ymK+IlTuSvSL>&Ogxz)~Z5k|(@NjP6 zJizxy^QNkm(*V1>@M-JRWu~g-1f*sRsS$y(j!m2^RjiT_oh?E4L)%ul7QTpJ1R`Rd zoUwIwQXEVv|H@T-CQuCV=@>8O&2g!=eh$s0(m1)JvM;uT>u?)-6o8Y2>4#me?^A>NYd zb3(~;ODiT%e5|~8@4nEu9PEe=N=OYP8hVNBg<4sc`r7B)jA9|*96q_!N2gdg2M#ywCuoe>y;VdbC{Dw> zC@g?R7FIQQdR--r7;o+sL#vLf21IbN#iVR=CzY!epLS&1DzKJ6mMamPj0b^ z>Om%_kFsLhkh*pEjdt9GrV{UdLLC)az@8|IC-NTm%5VehPfiSjr5kB2%jd7q`6ZXf z5XZ$Q2i)2ZfbMTT4DV+KUbgDya-DzN4U$R3LGxh#6%he)X>$~$|FS}BCHA;-rw)Wp zK*yH#IMa@hGHS^{wO61;H!(xPhXC3YGO0f~YCfG%&xgiL2*;CFd!NCtI>uY~O|ij` z9Q!d0{p`7!3{L;)IQNhp;)%!ngkmtev`$S)_oZj&m_P)+CXrSDpinl%1`ocs;N7>_ z5`>q_ht{Kj0BP}1C!*%NUo32&u|cSD`tw=Ay8wE&_L{Rt#0#0VIc0T!A>iRXA1!^T zAXtYCqW{@4w=hq9USc1fd^fH9h=b*2f)nZhU-{!T2D&c{DBr>WXTRx*GI;ViGd-|c zb#}72RZs~bGCZiBSYE3Z424W`G&e$N107>W&ZtLxr_-XQTa8{tagAhjf+Ksz?N6I2 zfS)RZD{tOvV#6$!=*s-xy;Pt)AhiQoHwLlXe;Tz_u!u>=LZ?nm4RNyoZWTdFt!FZ| zg;H1H$)#%vg(|Z`j(9R6YEwTkb123l~lJ5YY(uy}6>W$eKJTPDJe`tg5BsTp*${Z#U8PI5+{EWf% z>7Yl50hRxZ%kus_A$ULZ9M2!g$y6{Q&l)oJJ^=7 za|?yq4--+MZHlEtZ;ujcTW zuugssVzTA>ZUdnE1P!^KiaWRbR7F$v*^Myr{lwk6qa=0D#nff)N?_;lNwB_BYh;$@ ziCA;?IDs7pr&!_dOkXR9L@vDV(7}7&K60wV(Q+-}#4Ztxa-UE!;@ntTs5gAxW!TYj z88}eVz<7;;!3zVLw=l?8M*3=L(PWaoO*k_;m={0Jfg1V{R{5myx3A-q8x2}|fv?+8 zpfh2^ZAP@)$ojl1#sK7Jwa9|nV%0DTGrc%qHNt6yn}VP2N1jCQ2r}v!>uoL(dgG6^ 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