Repository: mit-dci/mas.s62 Branch: master Commit: f1b926db95b6 Files: 25 Total size: 158.4 KB Directory structure: gitextract_21borb0q/ ├── .gitignore ├── README.md ├── projects/ │ └── README.md ├── projects.md ├── pset01/ │ ├── README.md │ ├── forge.go │ ├── forge_test.go │ ├── main.go │ ├── main_test.go │ └── signatures.go ├── pset02/ │ ├── README.md │ ├── client.go │ ├── main.go │ ├── miner.go │ └── server/ │ ├── README.md │ └── server.go ├── pset03/ │ ├── README.md │ ├── addrfrompriv.go │ ├── eztxbuilder.go │ ├── main.go │ └── opreturn.go └── slides/ ├── lec01-neha.ppt ├── lec04-neha.pptx ├── lec08-neha.pptx └── lec24-neha.pptx ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ pset0?/pset0? ================================================ FILE: README.md ================================================ # MAS.S62 Spring 2018 # Cryptocurrency Engineering and Design ### NOTE: This document is a draft and is subject to change. ## Information Instructors: Tadge Dryja ([tdryja@media.mit.edu](tdryja@media.mit.edu)) and Neha Narula ([narula@media.mit.edu](narula@media.mit.edu)) Time: MW 10-11:30 AM Place: E14-341 Contact: [cryptocurrency-sp18-staff@mit.edu](cryptocurrency-sp18-staff@mit.edu) You are welcome to contact us via email. However, if you think your question would be useful for others to see, please file it as [an issue](https://github.com/mit-dci/mas.s62/issues) in this repository! Description: Bitcoin and other cryptographic currencies have gained attention over the years as the systems continue to evolve. This course looks at the design of Bitcoin and other cryptocurrencies and how they function in practice, focusing on cryptography, game theory, and network architecture. Future developments in smart contracts and privacy will be covered as well. Programming assignments in the course will give practical experience interacting with these currencies, so some programming experience is required. Office hours: 4-6 PM Tuesdays Office hours location: The big table outside E15-357 ### Office hours 2018-05-08 are in Koch Cafe -- all spaces in the Media Lab building are taken by the event this day. TA: James Lovejoy [jlovejoy@mit.edu](jlovejoy@mit.edu) ## Schedule NOTE: The schedule is in flux and subject to change. | # | Date | Lecturer | Topic | Readings | Lecture Notes | Labs | |---|------|----------|-------|----------|---------------|-| | 1 | 2018-02-07 | Neha and Tadge | Introduction. Signatures, hashing, hash chains, e-cash, and motivation | [Untraceable Electronic Cash](http://www.wisdom.weizmann.ac.il/~/naor/PAPERS/untrace.pdf) | [tadge's slides](https://github.com/mit-dci/mas.s62/tree/master/slides/lec01-tadge.pdf), [neha's slides](https://github.com/mit-dci/mas.s62/tree/master/slides/lec01-neha.ppt) | | | 2 | 2018-02-12 | Neha and Tadge | Proof of Work and Mining | [Bitcoin](http://www.bitcoin.org/bitcoin.pdf) | [tadge's slides](https://github.com/mit-dci/mas.s62/tree/master/slides/lec02-tadge.pdf) | | | 3 | 2018-02-14 | Tadge | Signatures | [Simple Schnorr Multi-Signatures with Applications to Bitcoin](https://eprint.iacr.org/2018/068.pdf) | [tadge's slides](https://github.com/mit-dci/mas.s62/tree/master/slides/lec03-tadge.pdf) | LAB 1 DUE | | 4 | 2018-02-20 | Neha | Transactions and the UTXO model | [Bitcoin Transactions](https://en.bitcoin.it/wiki/Transaction) | [neha's slides](https://github.com/mit-dci/mas.s62/tree/master/slides/lec04-neha.pptx) | | | 5 | 2018-02-21 | Tadge | Synchronization process, pruning | | [tadge's slides](https://github.com/mit-dci/mas.s62/blob/master/slides/lec05-tadge.pdf) | | 6 | 2018-02-26 | Tadge | SPV and wallet types | | [tadge's slides](https://github.com/mit-dci/mas.s62/blob/master/slides/lec06-tadge.pdf) | NOTE: No office hours 2018-02-27 ! | | 7 | 2018-02-28 | Alin Tomescu | OP_RETURN and Catena | | [alin's slides](https://github.com/mit-dci/mas.s62/blob/master/slides/lec07-alin.pdf), [Catena paper](https://people.csail.mit.edu/alinush/papers/catena-sp2017.pdf), [Catena code](https://www.github.com/alinush/catena-java) | LAB 2 DUE | | 8 | 2018-03-05 | Neha | Forks | [neha's slides](https://github.com/mit-dci/mas.s62/blob/master/slides/lec08.pptx) | | | | 9 | 2018-03-07 | Sharon Goldberg | Peer-to-peer networks | | | | | 10 | 2018-03-12 | Tadge | PoW recap, other fork types | | [tadge's slides](https://github.com/mit-dci/mas.s62/blob/master/slides/lec10-tadge.pdf) | | | 11 | 2018-03-14 | Tadge | Fees | | [tadge's slides](https://github.com/mit-dci/mas.s62/blob/master/slides/lec11-tadge.pdf) | Note: Office hours on Thursday the 15th, 4-6 usual place | | 12 | 2018-03-19 | Tadge | Transaction malleability and segregated witness | | [tadge's slides](https://github.com/mit-dci/mas.s62/blob/master/slides/lec12-tadge.pdf) | | | 13 | 2018-03-21 | Tadge | Payment channels and Lightning Network | | [tadge's slides](https://github.com/mit-dci/mas.s62/blob/master/slides/lec13-tadge.pdf) | | | 14 | 2018-04-02 | Tadge | Lightning Network and Cross-chain Swaps | | [tadge's slides](https://github.com/mit-dci/mas.s62/blob/master/slides/lec14-tadge.pdf) | | | 15 | 2018-04-04 | Tadge | Discreet Log Contracts | | [tadge's slides](https://github.com/mit-dci/mas.s62/blob/master/slides/lec15-tadge.pdf) | | | 16 | 2018-04-09 | Tadge | MAST, Taproot, Graftroot | | [tadge's slides](https://github.com/mit-dci/mas.s62/blob/master/slides/lec16-tadge.pdf) | | | 17 | 2018-04-11 | Tadge | Anonimity, Coinjoin and Signature Aggregation | | [tadge's slides](https://github.com/mit-dci/mas.s62/blob/master/slides/lec17-tadge.pdf) | | | 18 | 2018-04-18 | Tadge | Confidential Transactions | | [tadge's slides](https://github.com/mit-dci/mas.s62/blob/master/slides/lec18-tadge.pdf) | | | 19 | 2018-04-23 | Joseph Bonneau | Ethereum and smart contracts | | | | | 20 | 2018-04-25 | Tadge | More about Ethereum NOTE: Class is in E15-359 | | | | | 21 | 2018-04-30 | David Vorick | Proof of Work at Industrial Scales (note: no video will be taken) | | | | | 22 | 2018-05-02 | Tadge | Alternative consensus mechanisms | | [tadge's slides](https://github.com/mit-dci/mas.s62/blob/master/slides/lec22-tadge.pdf) | | | 23 | 2018-05-07 | Tadge | New Directions in Crypto* | | [tadge's slides](https://github.com/mit-dci/mas.s62/blob/master/slides/lec23-tadge.pdf) | | | 24 | 2018-05-09 | Neha | zkLedger NOTE: Class is in E15-359 | | [neha's slides](https://github.com/mit-dci/mas.s62/blob/master/slides/lec24-neha.pptx) | | | 25 | 2018-05-14 | | Final Presentations Day 1 | | | | | 26 | 2018-05-16 | | NOTE: Class is in E15-359 Final Presentations Day 2 | | | | ## Labs and Problem Sets | # | Due Date | Assignment | |---|------|------------| | 1 | 2018-02-14 | Hash-based signature schemes. Code your own signatures and sign with them! In the [pset01](https://github.com/mit-dci/mas.s62/tree/master/pset01) | | 2 | 2018-02-28 | Mine your name | | 3 | 2018-03-21 | UTXOhunt | All labs are due by 11:59 PM on the day specified. ## Final Projects You may form groups of 1-4 students and prepare a presentation and a ~4 page paper on one of the following: Project proposals are due 2018-04-18 29:59 EDT - submit them via github repo. (e-mail is also OK if you can get git to work) 1. Design and implement an application or system 2. Add a new feature to an existing system like Bitcoin, Ethereum, or another cryptocurrency or shared ledger implementation 3. Propose a formalization in this space for a topic that has not been formalized yet 4. Pose and solve an interesting problem ## Final Project Submission Fork this repo on github and add your materials to the projects folder. Make a subfolder with the name of your project so that it's organized. Also, please include a LISCENSE file describing the license for the source code of the project material, as people out there on the internet may be insterested in using this code and / or collaborating. Please submit PRs but Wednesday night, and I'll merge them. ================================================ FILE: projects/README.md ================================================ ## class final projects go here (in subfolders) ================================================ FILE: projects.md ================================================ # MAS.S62 Spring 2018 # Half-baked Project Ideas * [zkLedger](https://eprint.iacr.org/2018/241.pdf) * Add an actual blockchain to zkLedger (or, use Raft if you're taking 6.824) * Help implement an OTC market on top of zkLedger. If interested, talk to [Sophie](sophie.meralli@sloan.mit.edu). * Find the fastest golang elliptic curve implmentation for zkLedger * Blockchain analysis * How much would features like cut-through save on blockchain space? * Make a nice block explorer * lit ================================================ FILE: pset01/README.md ================================================ # Problem Set 1 In the first part of this problem set, you'll implement Lamport signatures. In the second part, you'll take advantage of incorrect usage to forge signatures. ## Getting started You'll implement all labs in Go. The [Go website](https://golang.org/) contains a lot of useful information including a tutorial for learning Go if you're not already familiar with it. You will probably find it most convenient to install Go 1.9 on your own computer, but you can also use it on Athena. You can use a regular editor like vim / emacs / notepad.exe. There is also a go-specific open source IDE that Tadge recommends & uses, [LiteIDE](https://github.com/visualfc/liteide) which may make things easier. In order to submit your lab, you'll need to use git. You can read about [git here](https://www.kernel.org/pub/software/scm/git/docs/user-manual.html). ## Collaboration Policy You must write all of the code you hand in, except for what we give you with the assignment. You may discuss the assignments with other students, but you should not look at or copy each other's code. ## Part 1 In this problem set, you will build a hash-based signature system. It will be helpful to read about [Lamport signatures](https://en.wikipedia.org/wiki/Lamport_signature). Implement the `GenerateKey()`, `Sign()` and `Verify()` functions in `main.go`. When you have done so correctly, the program should print `Verify worked? true`. You can test this by doing the following: ``` go build ./pset01 ``` Hint: You will need to look at the bits in each byte in a hash. You can use [bit operators](https://medium.com/learning-the-go-programming-language/bit-hacking-with-go-e0acee258827) in order to do so. Make sure your code passes the tests by running: ``` go test ``` ## Part 2 There is a public key and 4 signatures provided in the signatures.go file. Given this data, you should be able to forge another signature of your choosing. Make the message which you sign have the word "forge" in it, and also your name or email address. There is a forge_test.go file which will check for the term 'forge' in the signed message. Note that this may take a decent amount of CPU time even on a good computer. We're not talking days or anything though; 4 signatures is enough to make it so that an efficient implementation is relatively quick. To make sure you're in the right ballpark: On an AMD Ryzen 7 1700 CPU, using 8 cores, my (adiabat / Tadge) implementation could create a forgery in about 3 minutes of real time. An equally efficient signle core implementation would take about 25 minutes. On slower CPUs or with less efficient code it may take longer. If you use CUDA or AVX-512 or AES-NI or something crazy like that and get it to run in 5 seconds, cool! It should still run in go and pass the tests here, but note that you can do all the "work" in a different program and import the solution to this code if you want. That's certainly not necessary though as it shouldn't take that long on most computers. A raspberry pi might be too slow though. If you get the forge_test.go test to pass, you're probably all set! just run ``` go test ``` and see what fun errors you get! :) ## Testing and Timeouts To run tests, ``` $ go test ``` will work, but by default it will give up after 10 minutes. If your functions need more time to complete, you can change the timeout by typing ``` $ go test -timeout 30m ``` to timeout after 30 minutes instead of 10. ## Submission ================================================ FILE: pset01/forge.go ================================================ package main import "fmt" /* A note about the provided keys and signatures: the provided pubkey and signature, as well as "HexTo___" functions may not work with all the different implementations people could built. Specifically, they are tied to an endian-ness. If, for example, you decided to encode your public keys as (according to the diagram in the slides) up to down, then left to right: ... then it won't work with the public key provided here, because it was encoded as ... ... (left to right, then up to down) so while in class I said that any decisions like this would work as long as they were consistent... that's not actually the case! Because your functions will need to use the same ordering as the ones I wrote in order to create the signatures here. I used what I thought was the most straightforward / simplest encoding, but endian-ness is something of a tabs-vs-spaces thing that people like to argue about :). So for clarity, and since it's not that obvious from the HexTo___ decoding functions, here's the order used: secret keys and public keys: all 256 elements of row 0, most significant bit to least significant bit (big endian) followed by all 256 elements of row 1. Total of 512 blocks of 32 bytes each, for 16384 bytes. For an efficient check of a bit within a [32]byte array using this ordering, you can use: arr[i/8]>>(7-(i%8)))&0x01 where arr[] is the byte array, and i is the bit number; i=0 is left-most, and i=255 is right-most. The above statement will return a 1 or a 0 depending on what's at that bit location. Messages: messages are encoded the same way the sha256 function outputs, so nothing to choose there. Signatures: Signatures are also read left to right, MSB to LSB, with 256 blocks of 32 bytes each, for a total of 8192 bytes. There is no indication of whether the provided preimage is from the 0-row or the 1-row; the accompanying message hash can be used instead, or both can be tried. This again interprets the message hash in big-endian format, where message[i/8]>>(7-(i%8)))&0x01 can be used to determine which preimage block to reveal, where message[] is the message to be signed, and i is the sequence of bits in the message, and blocks in the signature. Hopefully people don't have trouble with different encoding schemes. If you really want to use your own method which you find easier to work with or more intuitive, that's OK! You will need to re-encode the key and signatures provided in signatures.go to match your ordering so that they are valid signatures with your system. This is probably more work though and I recommend using the big endian encoding described here. */ // Forge is the forgery function, to be filled in and completed. This is a trickier // part of the assignment which will require the computer to do a bit of work. // It's possible for a single core or single thread to complete this in a reasonable // amount of time, but may be worthwhile to write multithreaded code to take // advantage of multi-core CPUs. For programmers familiar with multithreaded code // in golang, the time spent on parallelizing this code will be more than offset by // the CPU time speedup. For programmers with access to 2-core or below CPUs, or // who are less familiar with multithreaded code, the time taken in programming may // exceed the CPU time saved. Still, it's all about learning. // The Forge() function doesn't take any inputs; the inputs are all hard-coded into // the function which is a little ugly but works OK in this assigment. // The input public key and signatures are provided in the "signatures.go" file and // the code to convert those into the appropriate data structures is filled in // already. // Your job is to have this function return two things: A string containing the // substring "forge" as well as your name or email-address, and a valid signature // on the hash of that ascii string message, from the pubkey provided in the // signatures.go file. // The Forge function is tested by TestForgery() in forge_test.go, so if you // run "go test" and everything passes, you should be all set. func Forge() (string, Signature, error) { // decode pubkey, all 4 signatures into usable structures from hex strings pub, err := HexToPubkey(hexPubkey1) if err != nil { panic(err) } sig1, err := HexToSignature(hexSignature1) if err != nil { panic(err) } sig2, err := HexToSignature(hexSignature2) if err != nil { panic(err) } sig3, err := HexToSignature(hexSignature3) if err != nil { panic(err) } sig4, err := HexToSignature(hexSignature4) if err != nil { panic(err) } var sigslice []Signature sigslice = append(sigslice, sig1) sigslice = append(sigslice, sig2) sigslice = append(sigslice, sig3) sigslice = append(sigslice, sig4) var msgslice []Message msgslice = append(msgslice, GetMessageFromString("1")) msgslice = append(msgslice, GetMessageFromString("2")) msgslice = append(msgslice, GetMessageFromString("3")) msgslice = append(msgslice, GetMessageFromString("4")) fmt.Printf("ok 1: %v\n", Verify(msgslice[0], pub, sig1)) fmt.Printf("ok 2: %v\n", Verify(msgslice[1], pub, sig2)) fmt.Printf("ok 3: %v\n", Verify(msgslice[2], pub, sig3)) fmt.Printf("ok 4: %v\n", Verify(msgslice[3], pub, sig4)) msgString := "my forged message" var sig Signature // your code here! // == // Geordi La // == return msgString, sig, nil } // hint: // arr[i/8]>>(7-(i%8)))&0x01 ================================================ FILE: pset01/forge_test.go ================================================ package main import ( "strings" "testing" ) // TestForgery tests the Forge() function to see that it produces a valid // signature from the hardcoded public key. // If this test passes along with the other tests, the forgery has worked. func TestForgery(t *testing.T) { // get pubkey from global variable pub, err := HexToPubkey(hexPubkey1) if err != nil { t.Fatal(err) } // Note that this tests calls forge which can take quite a while. // One way to make this a lot faster is that once you find a forged signature, // you can change the code in Forge() to start right before it hits the // forgery, so that runtime of Forge() is very quick. // The fact that you know to stat at iteration 2 billion or so is good // evidence that you've already done the CPU work before. forgedString, forgedSig, err := Forge() if err != nil { t.Fatal(err) } // make sure that the message for the forged signature contains the string // "forge" in it. This ensures that it's different from the 4 signed // messages provided. It should also have the forger's name in it, but // we don't check that here. if !strings.Contains(forgedString, "forge") { t.Fatalf("Error: Forged message:\n%s\n does not contain substring 'forge'", forgedString) } // report the correct string here t.Logf("Forged message string:\n%s\n cointains substring 'forge'; OK", forgedString) forgedMsg := GetMessageFromString(forgedString) // verify signature worked := Verify(forgedMsg, pub, forgedSig) if !worked { t.Fatalf("Verify returned false, expected true") } } ================================================ FILE: pset01/main.go ================================================ // Problem set 01: Hash based signatures. // A lot of this lab is set up and templated for you to get used to // what may be an unfamiliar language (Go). Go is syntactically // similar to C / C++ in many ways, including comments. // In this pset, you need to build a hash based signature system. We'll use sha256 // as our hash function, and Lamport's simple signature design. // Currently this compiles but doesn't do much. You need to implement parts which // say "your code here". It also could be useful to make your own functions or // methods on existing structs, espectially in the forge.go file. // If you run `go test` and everything passes, you're all set. // There's probably some way to get it to pass the tests without making an actual // functioning signature scheme, but I think that would be harder than just doing // it the right way :) package main import ( "bytes" "crypto/sha256" "encoding/hex" "fmt" ) func main() { // Define your message textString := "1" fmt.Printf("%s\n", textString) // convert message into a block m := GetMessageFromString(textString) fmt.Printf("%x\n", m[:]) // generate keys sec, pub, err := GenerateKey() if err != nil { panic(err) } // print pubkey. fmt.Printf("pub:\n%s\n", pub.ToHex()) // sign message sig1 := Sign(m, sec) fmt.Printf("sig1:\n%s\n", sig1.ToHex()) // verify signature worked := Verify(m, pub, sig1) // done fmt.Printf("Verify worked? %v\n", worked) // Forge signature msgString, sig, err := Forge() if err != nil { panic(err) } fmt.Printf("forged msg: %s sig: %s\n", msgString, sig.ToHex()) return } // Signature systems have 3 functions: GenerateKey(), Sign(), and Verify(). // We'll also define the data types: SecretKey, PublicKey, Message, Signature. // --- Types // A block of data is always 32 bytes long; we're using sha256 and this // is the size of both the output (defined by the hash function) and our inputs type Block [32]byte type SecretKey struct { ZeroPre [256]Block OnePre [256]Block } type PublicKey struct { ZeroHash [256]Block OneHash [256]Block } // --- Methods on PublicKey type // ToHex gives a hex string for a PublicKey. no newline at the end func (self PublicKey) ToHex() string { // format is zerohash 0...255, onehash 0...255 var s string for _, zero := range self.ZeroHash { s += zero.ToHex() } for _, one := range self.OneHash { s += one.ToHex() } return s } // HexToPubkey takes a string from PublicKey.ToHex() and turns it into a pubkey // will return an error if there are non hex characters or if the lenght is wrong. func HexToPubkey(s string) (PublicKey, error) { var p PublicKey expectedLength := 256 * 2 * 64 // 256 blocks long, 2 rows, 64 hex char per block // first, make sure hex string is of correct length if len(s) != expectedLength { return p, fmt.Errorf( "Pubkey string %d characters, expect %d", expectedLength) } // decode from hex to a byte slice bts, err := hex.DecodeString(s) if err != nil { return p, err } // we already checked the length of the hex string so don't need to re-check buf := bytes.NewBuffer(bts) for i, _ := range p.ZeroHash { p.ZeroHash[i] = BlockFromByteSlice(buf.Next(32)) } for i, _ := range p.OneHash { p.OneHash[i] = BlockFromByteSlice(buf.Next(32)) } return p, nil } // A message to be signed is just a block. type Message Block // --- Methods on the Block type // ToHex returns a hex encoded string of the block data, with no newlines. func (self Block) ToHex() string { return fmt.Sprintf("%064x", self[:]) } // Hash returns the sha256 hash of the block. func (self Block) Hash() Block { return sha256.Sum256(self[:]) } // IsPreimage returns true if the block is a preimage of the argument. // For example, if Y = hash(X), then X.IsPreimage(Y) will return true, // and Y.IsPreimage(X) will return false. func (self Block) IsPreimage(arg Block) bool { return self.Hash() == arg } // BlockFromByteSlice returns a block from a variable length byte slice. // Watch out! Silently ignores potential errors like the slice being too // long or too short! func BlockFromByteSlice(by []byte) Block { var bl Block copy(bl[:], by) return bl } // A signature consists of 32 blocks. It's a selective reveal of the private // key, according to the bits of the message. type Signature struct { Preimage [256]Block } // ToHex returns a hex string of a signature func (self Signature) ToHex() string { var s string for _, b := range self.Preimage { s += b.ToHex() } return s } // HexToSignature is the same idea as HexToPubkey, but half as big. Format is just // every block of the signature in sequence. func HexToSignature(s string) (Signature, error) { var sig Signature expectedLength := 256 * 64 // 256 blocks long, 1 row, 64 hex char per block // first, make sure hex string is of correct length if len(s) != expectedLength { return sig, fmt.Errorf( "Pubkey string %d characters, expect %d", expectedLength) } // decode from hex to a byte slice bts, err := hex.DecodeString(s) if err != nil { return sig, err } // we already checked the length of the hex string so don't need to re-check buf := bytes.NewBuffer(bts) for i, _ := range sig.Preimage { sig.Preimage[i] = BlockFromByteSlice(buf.Next(32)) } return sig, nil } // GetMessageFromString returns a Message which is the hash of the given string. func GetMessageFromString(s string) Message { return sha256.Sum256([]byte(s)) } // --- Functions // GenerateKey takes no arguments, and returns a keypair and potentially an // error. It gets randomness from the OS via crypto/rand // This can return an error if there is a problem with reading random bytes func GenerateKey() (SecretKey, PublicKey, error) { // initialize SecretKey variable 'sec'. Starts with all 00 bytes. var sec SecretKey var pub PublicKey // Your code here // === // === return sec, pub, nil } // Sign takes a message and secret key, and returns a signature. func Sign(msg Message, sec SecretKey) Signature { var sig Signature // Your code here // === // === return sig } // Verify takes a message, public key and signature, and returns a boolean // describing the validity of the signature. func Verify(msg Message, pub PublicKey, sig Signature) bool { // Your code here // === // === return true } ================================================ FILE: pset01/main_test.go ================================================ // You should not need to modify this file. We will replace // main_test.go after submission, so all changes will be lost. package main import ( "fmt" "testing" ) // TestSig generates, signs, and verifies to make sure that flow works func TestGoodSig(t *testing.T) { // generate message (hash of good) msg := GetMessageFromString("good") // generate keys sec, pub, err := GenerateKey() if err != nil { t.Fatal(err) } // sign message sig := Sign(msg, sec) // verify signature worked := Verify(msg, pub, sig) if !worked { t.Fatalf("Verify returned false, expected true") } } // TestBadSig signs, but then modifies the signature by hashing one of the // blocks in it. This should break the signature with overwhelming probability. // Also tries to apply the signature to a completely different message func TestBadSig(t *testing.T) { // generate message (hash of 1) msg := GetMessageFromString("bad") // generate keys sec, pub, err := GenerateKey() if err != nil { t.Fatal(err) } // sign message sig := Sign(msg, sec) // alter signature. Hashing a part should break it except with 2^-256 chance sig.Preimage[16] = sig.Preimage[26].Hash() // verify signature worked := Verify(msg, pub, sig) if worked { t.Fatalf("Verify returned true, expected false") } // try with completely different message msg = GetMessageFromString("worse") worked = Verify(msg, pub, sig) if worked { t.Fatalf("Verify returned true, expected false") } } // TestGoodMany tests 1000 signatures that all should work. func TestGoodMany(t *testing.T) { for i := 0; i < 1000; i++ { s := fmt.Sprintf("good %d", i) msg := GetMessageFromString(s) // generate keys sec, pub, err := GenerateKey() if err != nil { t.Fatal(err) } // sign message sig := Sign(msg, sec) // verify signature worked := Verify(msg, pub, sig) if !worked { t.Fatalf("Verify returned false, expected true") } } } // TestBadMany tests 1000 signatures, modifying all of them so that they should // fail. func TestBadMany(t *testing.T) { for i := 0; i < 1000; i++ { s := fmt.Sprintf("bad %d", i) msg := GetMessageFromString(s) // generate keys sec, pub, err := GenerateKey() if err != nil { t.Fatal(err) } // sign message sig := Sign(msg, sec) sig.Preimage[i%10] = sig.Preimage[i%11].Hash() // verify signature worked := Verify(msg, pub, sig) if worked { t.Fatalf("Verify returned true, expected false") } } } ================================================ FILE: pset01/signatures.go ================================================ package main // Here is a single public key, and 4 signatures on different messages from this // public key. With these 4 signatures, you should be able to forge a signature // for a message of your choosing, due to the fact that many of the private key // blocks have been revealed. You will not be able to sign any message, only // some small subset of messages. It's up to you to search for a message that // can be signed, and produce a forged signature. The message must include your // name or e-mail address, and the word "forgery". // If you would like to verify these messages, the messages signed were // "1", "2", "3", and "4" respectively. var ( hexPubkey1 = "b6f016f0b9839485ef0f73f894072246c171e99d45f1c83302346d96e2eeb6e04bd8fc3726a5d466fd9805e427bca7093129fa8fb486a4afe8856e4c8045a467628c376d18d101737dd1f3f2b9b01a15972eee26cd1b810db7cb94007db6fdc5cfcd45bb2c0692e9b1fc7b84453440f637430109c57b4075d2c7701601c6141890a13841c729660d8da005d7b31b81e05d7c3146d5b1069089a8bac2e40507bb959e77a4ebe64cde80168104451f45d41d87fa80033d771777799fb2ac3081cf0d6131143f9f1a92d5e551af68df61900f6eb7a4bcc6e2193e75b399d9a26d082a938429f9ba9232272c50fabb8f3a497995818043a11189517319a6371f13fb0989adbabade295732c03444836c97d1379dea18837fb0560cf84808457447599b251e892ac2ecc65e825f55e49922c179abe4f07c094449c4b1018c2f3f8c6cc599ea88b999bad603be8e6bffb990b92492e3df165ca9b2cacf86f481a738179154610797b6f550762ac7b80d77625a0a82c32acc04f2bf4d66aaf9075d66d2752c653a141d9be51b6348847083b1f271a8033279704d34e142096ed62f16d5f0fd43a7e4c030e08c8d351c8b500a9c54562b1add1548d72fc9d8c67db5ecf7b0657c4a9a66451d9a7e304f502c9d0ef99d22d7269b8813f0297da19aa53097a6840db41c619f9bf5506b681076969ce65d8469410303a252d3590ad3683e1bc60c636ce9eaee4652fa1fcb12e5b73a0a905717689ccb44292cbcb7c611d299c946311ec33077f0273d45c8cfce0163f7989ce76b833ad3ac1e36c877ea4c3136484cba975c7469439834c03aae5020a54229bf52e7a6e2c8e0d37c9197e73c1aef3f18343a7658a4e560b12b23d597eee586f88ffce15e83a3ad5f664a84a24e40b0f3d9f8db30f3dc271da5a0ea33dafec406c5903d569f4b565bc66f4645be2d7f690af664e857c6a6636f779cc83334dcf5a925beb733b4cc6389cc7878030ba9d3ab2b04640d7da45f11ace0b3a6cd1844857ff959950f1ae67f9270afb26cad1f7aa4d2b41c7369aea6ef32ff2fc2d7eba599a65c491d34843fdb1fdb787b0c0e502427a542e87cb8f3947132f1a88196aa910e0893512b47bcfa4a85a9c640b8093369837692fb95e5d86d227d812f5532a006e9ac3c53e7a4fbe172fd8d3d9ea325c9e06d23e1439f4ffdafcdf04f653a541f8a427525531a03c37de1ccb9adfa7fcda22f20e9a1d8434c1fe58e5e8cb72845d3b26c4493c77c2746017b5f37ea8ad19ed0bb11b64f311c571904a03f9c0f713442149f6a8050c154d516c1dfab985fb9083ec10551e1f2ca5da3803f3f604413a969547718b764eb255e0cd2e05869a1bbf7167922a4977be914fef4d76892136169b50d8d414e01243f1c6f43381bce0c9146aa836f9e60b226905e66798a06978921d123fbd765fe41a22aa2eb0dbf9c9e29f86548002b1a1afdf478eb1b871397223336077e5004e1593a091f26de123c2df5cf105fec5b343e66d405d3e4a5b3d45f92c3081d3b6556afc052327baa1e5c6cd9646730cdd75878cf5914e845ba1a1f2b53706a400eab03d75aa5bbf3ceae9311b45f20faa5d0e10a1612bc6f8a35a43d430285ba7380538f112dbe306e024b5c3e96bfa63b19c0b523bda1b11684635311bde23afe18e8ec1e60210add3629d5765d7b7d7cf9f746ab330fe119f5c6ca5a1254ab3477b410078cb6d54fe654a2134ab3bea4729df46685cc9fcdb77537ebbbc25a8cc79002dfc763295ce6261104180bc8819f345f67a70873b4c0ad2b5ec5f412ceede59e3f55bc012a82285af4c496c6c0e444fdab5b3ebb9e09062ffcd4d13aebf9d7b6b94445347a7ef24c442e2b7c880cba94f3d2aab3a25ad084e8a8613e4884697ad8a8bc3fec3c96fccc4f92eedcedcccbc61a43d33c867cba6abdc13b83c6ac4713563d778350e08adfe1ec3e77dfb1aff35744d84e3b0046d4f94df29813914f60bb8001c1a08d918ad87b4a56273dfa92a5c2111891541b1b797c051308de00782034b387d34d767618cdf04b366e3bb29c2c1fa83fab63e1d18aa54a72a68a968427af63ec402f1d726e4287c061d5781d335642c0f611accdd28b288ba474c011fafbc8309d6435c267f9b67115eef26311c056102a92d159dac8d9857a271e1e7901600446e65601cbd3de4721c04d5a5c10b402ac50cbdd3edeaee99a08655365e66cac25a598629a2b9dacfba57d35e70ad79abcf04404c8b61d8ae05c304c1d880b1ef7511110029bfd73ab9c6ba9dc7c6b9622e291c81797ad5bf109972ca88332bcd4b8df9f8ba7a3965e8429c0cb29c1a17ab97e2efb8906a94f5076fa041c5c69ee18f14cf36932fa62b76a5ec28c7b4d80ba93fd4ec1e34697bef59519dab4b1ca32d28420eeba01514aef61a76efc4033ef810de2750795f12968de6af646e9be4275d63c40eaacdff21aaa4423a43a1cc6a57af6f77e0ba62f773a50343b3725400678c802c39eb677f5137ba73e87a9c17ececdfca4aa559beb2be2d4dc83a3df02d87123186af3bf943ee132d8f46a8824585cb9fb7c36d0dd1369d227da30de92347f4fb989538e81e26adc319d0be17668ff971cbf95a84b206d4d6f6d337be53b2510a40ac132814d84a516545ce51508949528fa8c23e08cb8072508521517333cef74ce3e7c768c048f0862a7a30bbb4a9ba49378705bd0602ba232068b67d8c11b12985ed89226544b5eea5416ad3c3cee95e7eb3bb075fa8128ae9959719f52e5e1849ef307fbeef44648c14e194b6f94ae9ad3c10660e349fd9b00df4e383517ab537bddd58e77883a45724f58b2050a7b0f723e5ce5ea6984285fc52ee2c2e8dafa5c07a340c22acdde11a1297c924d764e4af3f0ec8c07ad5bec6441fdd70037f7fee705faa0fbcdef8bbc24ddd79a4cc9de5b53279bc90d87b59fbea65442ba0b177f30b7fe204d624acc8c42380825932eb1600df5338f319f991443f36582b64933f2ad952c862939601fe690269779e611a738c3389a0cd84d7719a38d9349ba672002e1717ce6dd47664cc8802e40eafa00d7ea2f598b80caa155bd11847a274d22940c5aee6038011b1d2a942872be0c5e03802e13f49c19d4b3f750c15a38bc6453568aeaf41ca19808e32525c540fabfdb2153fa8ccbba0c830ada26ad511495de2c919bfcfd69f3a420fed0e3db901f812f33a24140ffd966612340019f2e1ea39c4a12cf9ebcc8ce513cadec0e2a8b10980c2fa06fefa1aa750d0d1746635ebaae8c3bfa9ae8c8880a3b5e532a9b7cbab8e843abc3a2164a4c7716099192921903d78cd2af4b92740fad3841179ef510b84e5ca6083cf8f3eaf7dc3f90ece27ec02ed7d8be9c93cd25ecac5be1f4fc3bc554156efc864a9ef829070995770489f951c9387f31e16e478a88f498ac21e331d476111d475e90c9f5d800b5918c98b69e7b01582a367b19a575ad5ad391c056f2053ecf62eae6041f684695f751f0b4aeb58928526b5750707d7c0d399c49bb46e1a909ee5c97a41a68691cd263d74902a1dcb6c5876498e97e14ea9949d826a66ea1e54c6c8c76fa339226b147a9e06d162fa2d966536906308c77a9938de1af3cc85ff8bd4477a6af85f5cdee121c931ec6571ebc5c44b1734d76af70cb26a1b68ba3c0dbd09d058b8776f2208561f86a8f3f637b74000c68eb68678aca82bee671dbaf2d0eb4ac818eafb5d8257e5077b840d750ffd02d13055b9c40c54715691d3c55cee990bb0c80d75f6de9bdc5d43678976b002adc871c5b54d6993ec82959b84e9ca7c4cec300dee22b6766b0d67d33465f10540f813b8b772746d18c3712320865ea1db8ebd58be5f3d554a0cc2825816b381d4bc65034dae57a02bd532f6898e03cb575e06e8d79828cbd0235274662719f7475130efab0bdd7a4f7c777c9f2f1f48114058f475f07ce4a533c61ba968cad0c6cc5fad9c12ef0350f2932fe23e7f6d3d33ce89e759d288b25240707b9f31132833b2b933124a78d5b6c63af99e1bfd0ab159747382ded84dc747e105298feeac3448606067fc015dd3a2993d8268ccfa2d19bbd69028df5035cbfe11ed57ebe106063fde4508539f57cfa389cf774fa12f94334086bcd4c50aee488edefc460605d24b2cacf33377c5cd60768744b45e6a285500bbc9f45ea5bdafb3ed48c4626b7c133a6a8412fba22801923a3ca1c25fc97039e9fe730f626f655407a102e46f739b8bda61fde872161eb83142f2bb6a0b466e7acdc3000ba602bcf9fd910bc8ae09bee67abe919eddc04d6958cf463b7d903c03a9b43f2069c94dc56ef4c3c3575c658d427dbb1077a2d066742bd498a07607ae0b264f6b9d0b709b6e50ab41c8e8b8e39217e039b4bceebca1e756df1ea2bf9c9551aea6c2fc15d98d20a7f60ed580697948a23f7c5b65688c1ffeecf9931bd2610a240c5f900f5e12a2c87152cc46ed7792b3f6bfc94cf9bad80887d702449969735fd33fd144cd5a53c86d84298a936ce770409197604cac920b7b1bb2c9630f4ff182844f0074207c863ab8217ca93c1d1a09f5b582f7c63ebfff81096983d70be249b950aabf046db474d99bec76e3cf68e444a32fdc9a8b1f2da70cb68cb6eba06bf0ca53ba1055cee2775a2239a0c834f1cc41644ae3c67bb28243056be658e001bc22a6a88f08349ea023e4b14194871afac0aecbf8b4952bb6a713b9fe34caf78c97b351e808667904818de46b999090ad785248462fea4c28982351b36b053b4c272f51850f8153d0822df083c05f227a41f5b863114b12aa7aae1f0516cd4ab09e9a2d846c0cf2408e17df9b0b64784ca1cc855276b8b0c2308d99a3844b6c929e0ef0d16f17be2dfb4a5320749f37a00a16259c756dbbe5db86acaf678b0e99484fbe01fa05c076e90a45faaecaf84b40fb7b11322eaf2d4bbde32a9ac59d219ea181f30c553438d1d0418bab10c8da1406d71ab492007b92ef88c97cead52727306e770eda50bdad4c8020c71d4fe2c08445ab307e0d29f2abbd43fefcd4a7898869a8ee0fe5492a49881bea5ed7754396ecf4e5919404d34862b606bfbc65a3734d9ecc9a61061fe3b4ca67c2dc4f843dc03a59e110a5effa80bcaadba7a8b45e9cc418bc0c52b7df3885d3098d7618fa6a4737cf276d85b22ab704532de492cdd04ca90d98c198aa3e402c41e7558cf42c858e6840ba821be18b2ef6d4b240de795e2dbdf926e223da668d04a528cc6dccd3aa72dee6441fc3b39a9700eb0f7e29f202573a234e9a4a73e1892b8b4aa250f54b8149e1959cdc5fbba3472823cb1cf4fd50f4cd86b0bc1cabb3844edbe8f1d1388f1e2ffd10738c1c91600a6104b169ecb1f0f118f32753596c80cac6754854ee4e3612f74bbf3b4ca2024d63429825bc8d0bc6554539d1db7baf4361d1785b75b85cbca3d9a1933feb7257ba0d0a74c26d328f5110c3c60a7c93074f3f0cc51c665f9e7c05a78293c76e7a282890ae34ecd20901f089bf6969ec6d32d130db1b7a4a85230f45fcb4c618d8e31048311191dbc889bf689f0e4f43f226909af8a2c3a5633d0880c051d46cb7fa43b650b2fe4426cb034e075d7f3ad27609ceecffe6a341b15d639a8dd15ddd2c06e33a6e0b5f693e3ec2118560336bf2c936eeac6aefca0a4c18fa6bc6fc6d594c7615b84a070ec4e4daeac6ff74537c65d0320e7f78eefc449ce53408bfe7f0f7a930dc8b5e70cb7abdbd7fd2222aec2e802b902091c58fd62002d1550a105f3ff4f357b9540491ff83e9682fa04557510066f1767972abd36b02dc7e56a4be3c7cf876649a1eee96cc5d4b10344f5374c33668587aab622cfebb1962407010386ac557126689b884d17bb536694122dddb344f04565e82c58e47de0d38e1356529040ad0e5780219ce15ead5bcfadb143787cf5f712f58e13ca06b4a0de4d65b1b666163770c283c5fadd237d306694d25d923a0528820550181ffc7a4609e5fcd4e20657ae16572e09e31f93bc5e670e585abb0d49852f0d71a016b3c6446224f3307c79fcd8d9ebacbd7bb5840df33cab998d11798c6f04702c9901ceb80d7a106280927c96a52b818c158f78594b7d965312c05b40e97e48d119a6fac5d886b37ecc65f05b74df3a5a9556901ec718a3281d74e4228d463a690ea01d4af1b0a1e1cb5d2278ea4658b8d710e652c8b000a1ad4bb490dc7e126a389a459bca29370b7acfc5df1f8d3d90f2da72217036d07e8d458d646c465aba83d8d8247a52b97346922327a71ec08ef061abd9813970d137b81a9eaef7923d7a23a9bfcfeb4d655c3c684386f0ef914c8b9d791ad4a66bace189418d36374980dac1a205189fe60b2dd7e98dcd61a478b60cc2d4d42c37f24da59c7488101dc5850239f91d27f313de8f4f38fc21355e0ac02ee9dfcc3a91b6a3fb773cb04a9fc0761a7a7624eec08cdea32cfaace2aa5491715930dec94bdb2279051c9e98d8cb049ff01a7a118abbfcc98587ed3b315fc40148f15977de5dd8b17aaad7eb2416693c6c9fe2ebaa337b3df2b7bf8af92dfb78268aac7fd41525bcdc4e983419fc22e16dc093ca8f2b378f6ab5540de3e4d92a8e0d4e68472939e4372d8dc5a790eb0de965217b28064bca71f29770a7a9f93e76d1843571177aa66e2a526f99c826fc6f02e31ac2ed536732d165f7448d9af749ffb6d45121102a561304ab0b8630afd730ec10e0920717db3f39732fd40f12ad1926362bb6df0032dfa031d9947ca83965398c6c123f54306eef6648616be7cad6f56e6401ba2cac3c4b2b7387bbd227d29d4acd4f03361e5040b970ce41bc62be6d69d985b9420ef94bb2059b94f7bc4530467f47345239e40e071977c519b9ca34498ef70793106e44444f7071de772949d80968d923ffad7017bbb038a9b330941509a20240abab02c9635a862ed2a85f4297311242ccf247f3edbab2821817ffe10e53247d8c760adff363695631069466114a1a325d55cb5ce058035878118596e7adba6ba16776eb035a79c8d1d47648016887d9b705fea7eb7067ae1786f9e521067a89043543456e141d06c9a97e00ed3f9aa8d32b0de071a7535213c8aaf3388fcb3ef06ee183b284581e0b7e7b9f2e30b53bcc098902a8254aeede8de1ec57748f56f3ae0ccdb38a440c4a89279e01b38a6d125343e3e0641461acdfd21f4911547e2a7d505e1c3a382d683883d544335f7c782581819351791f95d47fef49586adcfb3dede4b1362becd96f54435d424084f181d219493b29386ca7b02f2cb8f686edd572597db46ce43c3f8ffaac67096b79b8fc6411a61bee33b256cba76b6227aeeea95a7b69b5111c391e295b327d2f5435d09799bb66c1ed876706f85638bdd8eca7874c9612631f550b5497f09505c257009e04558615e300250a04171044cfb5b9c5144c274cbf2329ed0f2941a69c34ab02567dfa41c9c7a264e3797e73032f7424dc9eb87560013c315d9bf735ae96fc3699adb9e2c91d5eca71b479f03f3d9dd7dcd72771c0daa45d8982eddaa023f7d6c92391300287f02433917af4cba3b738b43612fdd8880774d4848327612bb2bde989f1529db026b7846265ad298cc8bf1bf4fda52add2f2660fcbfac6b047cfd74d069e4000698bf51e1024cf5c27951230296b4d2b7df0674224ec8e9bf73629ceaddfbc545b5c37de811c2e34467e7e0921044fc0a61fd5b66f6c50c274a9727806fc8a49362626a8a4bb2171169623ff566fa9aff8b41b069ad5f40056aee52146f95ab6561a16e8f57e089a79a45a12942db117d43a53ffa87ce09a604244078cd49ea9348a930ae398972d7b6b87f0851cc3b373201030a70a9347d762e6d571b1cd470180796b2f727ca8a5762abca270330ed4d0ed9e21117c0f2d2eb2438074ecb90fcfd2621d80e1cd3f70e578a032dc945336d784e61493fd95ad23314e301f2f541929bc76ae7e93b302c4a18788d2c4f3405b8df322166333e88759042477b6068d417a843ff28e796aceb1e7e847e463babe25ee54e7693af7137155ef11bf39082e3c3007ab59993b5c51844bb2b759e88259d7050eb64cfafa26689c8f167839d1ab4fdf8b2e7bde292f67e0fd7908694762fde429d364211240d1c42286b98bcfdd3de98c8b93d70a3124aacf69771c8725fafce657cee16a135b64fde87d2d2db41d91b50426e0fc7b817a5a70e728e88a8ef59415084404ce0af04fa163815d224f33acd37dee7542f84a018100fd56aae03bcbbbbfca64472c65234a1e04ccad8a77a54965d5dfe3d9dc134a543484c857f6d5dbfca2871a5d3a8581ed7657e28ca1c00de9908446e0d5f5e9b8594ba49ab52a0a4462ab4e835c4c2da21b7a149c2389ecb33b8c4994831c229aa09f459a8ae302033215acfe69e12a54a91b0db6b227787489e3d96c908fd1c23be4548b7a8a3c3f3d54ed06cec581bea3dc239588435bcdf0af718e518d1e5801114ee9176018fb7d8a6b4af9a9a2142a6fca5eb57d93f0cfa2ef0107636c67ed5c47172b7139711f208683349ed350429afb15bb6a5b357e9509ebb8dffb776a8fcb998de40ce293fdab36c69b71531ad699ae02f1beeb489969243fcf8cd841ed1e2c48d7e73f3d432abdc7425a6c52d20da22caa56221490f9bd60a8c63032b34ef6b22822448f9b7b322240d960cc7f3203f6e5c5365ee580e3d473c52a1c20e3159eee3a02690e926885ae788b0590ba034f4a6bcff473926ba2c4181bb66a4d6e5971b1fe33635ad34e96b2bee235c1e79d7e19facaedb32921f6d1de7d70c0d7507514053d89d80df1a6143be5f96158f68dbc2ab738f7d2dd0523c44f97e1fcdac38caad88958e38d5e16b5681816684303291a7bd85113731c26a039e96621e4d788bd684298a526678a41cc2d568596e2fa91490ef86f7d7a603909b41f034997808c2b3686a3c9100edb10b5a0e718ae620683c86dc947df3f7ae78961c17771182d9493a655977ca1d2ee84fd0806a45cfafab5c4ab67acc440447a962f606c372daf3a149596e161fc31c10a4fe57c51c9225cdf55d7309b53784611511b239094fe8732f70f988bfe908859adbaf46e7a096003c4a61f01e17a5d8bc76d7742662019cd98d82f9b4db8b09b8f287b5a9c270af7ebb09e9e02604e12606d9d92296f7cdf2de28255ad9c1558bc03be47bf6b9dc33449392722d1e7b08178469ecc6635809b4653d8a0a79fe316bb10dd0310769852de5e76b7470345ff445155898189de37d168cf73877fefed3e7d83e5402267179df6ce57edf26e08e55b5f5f7637057f615840ac6864a99b3c87705e5158fc42a0a33f8d25ac899d88a8608ba3e4bff051f4ce17c5a6231bd738fa44b099c2457acc0ffe02492a81232d83bab62bc64c1d5b1b2c98223e823cffbf6af9bcb9adfb0dfb0c3dbce0989b5b44f93622371af967690c4fd6d479f3ccc11ab18c1b46fe15227319658cada471ba8c8256c03d590041949766652248c706b414c7badc42ea278857936b6411cb84ffb43013dc9fac5197950d5fd29316613214ee00e8a4943353b18ba23409886a367ce2963e8bceacb145499a5297e1e0a3b77cfeeb3733037b6a7c4f9160819b6e1b07d1b0883533cdee80b94930b71856902b1e7a9327d1628baf70c8d961e095f1ac1bc891374d0e928728008e88a900308052262c739e4a656f4c7789c74500e7053dd75dad045aa744f0582d9abf0495f17903b627361ce9146e1add2ad6cf60471dd26e5b5b5a95c9f18f73b5a7650e9eb8ba8c5b8b5376011c1870c6ab9ff913e21bd92848c4157a72d383db7b1372b4c7ac4fc88ccbf53894b71fd285d328c34fb72df305ca3e35cee6d9c4f23383b4dfb8b47c87d729442b8ce11dde793eee5ee65c5734454cfb8d9e4ced705a66abd556f81af3401b17ee1be62bb824b95fc63536581f3bc25f86df488217bf21d0efab6c0a80252062aae8d542f8a4a1844f1a45760a11eaa3947cbe0f810b508355545b695d44ccfd1fc5895c0fd40d4bf9fb830a18c6b1c658e8fc683da89d1ed3b6c48fffade5dcd0bc9cd97243127565d83776bc951b340bb5dc7dabac19ddc27dc6dcc7382c2c9387ff14b3f6a33d78d04c40920d82af2436e4051fc23777a8fc2031b217d373659b6bc004e5f4f1cbd3c33e3fc6d7126aec42ee127889884ede45f6538d4c51b08ac965c75f23d9d0377cfaecab5d1c897c7bd50f0f31901e5260cb1eadb5abf5f3c89098c4cebed0bf424a0ba344eea67d6978b1bea4183213e561e696fe4b10e7543cdaad70ea1cd054040aeb04620cb4e7879bec94d9fb3a9ee7a16e370c34946022fd06f391d66d0f13512306d73a8e56ca4bc7b4701d665f914eeff8048fadbeafe46b1124acde372808cb2278c8061555e540b4d6c1cc3476c8e410d4ca3762c5e858c682b63166fa74796c4546afd881ab453e9d5868fc8877f97f0aa2edf0e6d959657d1785926bca12153df613255206e89e53796b1e53d4cf860d3bed977fcbc3dabafa3b9ce1ebca0ea28ed55ff48d74b01ea75d22149123f9380579b2b19992a86a32a5c1d679b753ccc08d0a5b949d2ce436d65f35a031d817129ec8bce203b38173c81185516ea824c0c2ee08ca7ed850bef73afca49a08d4d82d55ccfeec20eb674e17e563ac70ebc740794742f100adcc7000108efba6ac05f976fa1dea12efc65126dae58d629155b3d90afb29e7117d0e30d4e5c772b070228c7c1d2b8eae20c14081f40d63755931559427ceaf560004f74ac7dc38f36e1d7e87a60c657e1d12b24284e4bf107d709836bcb83a6257894985218d6bf50a6a49c0c00205d25ab855a27d4fc38182e6864284b215dc44ba0c27b7e71ddc78ecc17d1e31d7c3b75647b37b4c0f26547c741fb98b0e96f7db9f53766497ad80d4fec726737c779cf0797fcccee7f7386933198c649962707e438fd7249d74a0b9724793c3bcc74ef21d72171673810bf826b03c988d615b4f372aefe2e23848431a9afc4758713ad5405929aeaaf8662f28d0f3841251da1cca213f4491b41d097fb71a9dbcccce8e800924b7d22e7c02ef2ea9da7246eb5144bb9cc5316b8f26310bff4eb230a44e4b302fb7f9dfa69e805afb25367b77b8a9ea0463fe83b165be7c1756430d32cd8071289b88f90c3fbee880c2fb3d5d2cc7411b0a6adea4a69df56360bba821422af09365321356d1e6b4cc1d34452cd13811c763754dc88e80870a50eb52db1809d2986b21ce6bdd54de3c607dc04873354f66d212cc51e09b831a529252f60d1f6081f041e46bae2f4342c812d05f7bd67e4097e2527e5060e62d8ab34873f1c65966152c5e39054b2111087cdbc207c88ef5edb1bfc4ce8a0c95ecc903f0b34007d7931f6a9283d579020f52e1fba549410477090d6da74f78ddebadbf72e6038551aac63450c963fcd8a621384ffe70a11a862f0e3c11132ca6f1633329e18f02fb3f70259286b8655cc8d98aafb6d261aab62ccb31e3898b2ff26e4f4ab2b84d0bd71e6ad320c55a7c5e7dc71ccd82aa2a8091b6f29c5db555690144b993f3cd31beda060ca1de763bfcb3758aab80ca743fe0af386254aca729d37b5ddc8ce068535b68ff25745169078295398a9aae22979afc8f5fac36ad0e30e3da01909a9f4c6b21661e536632ac37253e2eebfdcab15c37e77b714df568eccd804a75b75d407b8228d98092a5f626ef6d8f21c8ee3aa31c55bca7d55a4be9f35faf7f2be0de6185c5a524d0172a9a13c16dddb438a52e5d351ec597661b45cf3c2717108008892365b08427cabf571123117c8d7c923dabfab370c6398381e615eb9fa536c96fd96a4940fbaf5b692e11e6eec9a90e057bef39719094fe5f59d75fa5496a247a09b0069ce054048dbd223ba5b872561663fb563b71cc9a6688f050d2706a94d7e9adf758404a5c6b39ec4b954e9886bef28b83a75319fadb2a1121161c2900ea128c2e3ce3a562fa149c699891c953a8de69e34ca48fc477528995d9faae31d3352a0309001eb2b9f8528bc3efdbc80c278b763cd831341a343650dd38cc519557a32f0c6f43108b0c6947f50edd3b2ea9631fb9c2ed578c303b00b6975acdc3d83a0760eb4f7015e8f8c126f85dbb36d2f23caddb4d4fb05a6d487262f19edae90951bf548fc76758c154bc49d549dd7a0a75b548f7cd8e70d1695943805477b1152b33cc6b7391d608b2aafc5e1c427a7c41d2cb9b81ce60125f0b250af468b80aba7cfeebc875785f63903a6dba351238b910a76bd7ad6b054a2f67d4174437d9707c91cc7b7d770ad0825c9340cd5cc920e6469e4e2cf52fdd074bc2dc797f8d2f0e7fcb34ad946425794a1612910eab086576db8a2fd5f8732025030ae7521670a82ed93909d10af9f6c546f75c995238184a245d6bcddcc949335bd169fb57e79a7a2a9b40f4a873a15e4639c2fed6414dd0696e18c8a718f6609325535f495cf98185b682914e2dfee505451432c318f8f267a65fa4a9d983e822b125105ab7b46052d3099cc58b0e73002a6c3c0d438735d3781e6ec5823ba5584995f16b47483fadeb36401f701091e227d1c9865b0afabf2ddc0d8c30f5e3ff9d0c2db05d7001a6044388ec238564521b4e3d836c4090f5e4eb9854dd43ecb100f0945ca1d43693ed57d731834ea089251c748d2f40a1911f590b569e9c324290b88e1013db9649e18391daeb19b1d949dedf75508a32f92b8fe54f26fca5b97e77303ae8dc529c98345c5bae54fdd437255bad801c11391f01f3fdd56752c8c0e12a2235795e7ec32f24d95c5f4bbe57f1a12884b6d7d6ee5b0650cfb020ceb98acc71ad6906dd22bc9a94b10777e8a348cee774fc5826117af39273c7d6bbc804dfb310f8fcabdf49cbf2030e133bbe6c6bf52b7ec31f3a8d71b51a330d4dfcd1cbf0257b5d528f024aadefdd9fc00b58c9f54fe661cc05469957b9b6674094e81b0c2fb0dbca264d77bc25ffca32d144466f9df7b694d9336e33783b4215dca6b0e3e0780fe4d7fb8819ea993a196f76989be8d3450d9835575c1b235710e91cfd20aecf2cc892d58395ac92a89f71846e2e35243db767a7cdb58f36562a6f6242da9c9f9e8b597bdbfa3a2828b270300629bb14410e982c444abf3aba71e1bff4bf985d1d89377c8bdca42f79130d27e7eeca98f6e0c2d31710c2b6c83203df6d804d69102bacda35e71165825807487be6f69f1778af252b5baf69e0460e0ba2cd7c515f44949f4e61ea65417e15b19b520e9149dabbfc47b75ae5b252066e1655ed3fba15c360385f5de249b70fd6a5115f5f324f7f5eeb99cecd49216d634c3f14ad2a93c925856ad41ad7a4fc4e4dba74156ff66077956bb37233a1b89212d6f0552ced14936f8b7ad19e3ee83a124bd40d7ab3babbfbcc12c85225735b141e8d59195c15676b117d93bbe0fa7933b655e1593e05f788ab4719157026a0b3eca2d42d093e35bf00eb40b6d37605d5a15ef13214398a35a9f0632df490b90febaf4466965382dfb0248fb314b04480bb97ebce2d0982e6613dab0934fc1168f8ccf2f6e9afa8b60a3aa5152847020ef6565b61fc0d881eaace4aa5862bdc21598231d79ea6925e66efed029b5ff65e16b65397f58735ed0d60739e6f07a030fccaf8e355be7ed7458fec0d8f3c7e6842fe2761796df3fc898f916a04c83c53099fc7ecd44600d62f896f7ae8bba96bba96522b40f065a6b4f45fe860b1110547f242ee7d340acb90411155eb5bc094443f54f0025a3b1d61b8733f8bfbc932d6d0171da4600dfa932bf3133c2b9e5c7bb4b165d29eb959f266dc55a2e25d3c13ea67b12d436c240c95a9fdeee6be8ec6272787a35a71ec070c2361cd79460b5156fac4f8112322b7e936658fb1a4ba633010bb88e2b7afb9bdab9a3d93b757a8831fbe7367f0058b3e69566c8bb1243b49e0f849068d35d12f422ed87ef599e2c51a9100626e14606a4b8a14c8558297ff8a9c4564e3189885ee6a28212ab824a04b3ac3aa314da565c1329c27cf81dd3f93cdca2b3b37823552204c542fc80a0fc3e7519e2daef8eb0006fd9f9a0d8fe886d58873d2e67b8af7585802424a7c58f6cbda186808e003d720e6405ef82095c82eed087f128704624d06f2bea72f61fa2c47743a7acb63ae0344bd9c51fdcc68ef6f8ca3b8437cc1e83b12c202bf3648caf752a3249de9f194729bcc89093d95c5f074982fd1e720ead9401cb6e2e53eaa3e522ab37e336daacbd64ca4bf6a5000376925196e0be477f5202b86b246c34fef52506e76792a98592527e47f32b26454eac9a529f28177052220d33eff5fde9a35dc40a521997e19da4eee1a616ff969a5e16a0d8a0ab173acba33a606702c581c42109227a8f87df07156b8de776893ddc7e595415458b1f39c69e74f686f756658569ee7e6b443128b8b8a55a99e150e7a879723764ce2ee07c70641f76097040fff8a4a01469ebd6801353f04579ea0d85acc6731d2b6a4933498de0d6ca7248fa4ebca326568836da4415b50efc07389f474250741ff6843db1cf3f1178534fcee1ec11cc5744fcab7f9e3ffec1e22904f4bd4d0a50a388037d9a43914848dc1d05e6b306dcd3cfd8fe16c0528efd36c24edcb2d6c2b1f6be5d761754cb09319163e0c7b2af97850879131103cd351caba6b0efddecffa07ebf0ece5c8cd3af76d75cc84d7157e53b222cbbb38c3b012d41ec426f4ab46c5ca6cd50f63fb069b80924612ada32b2d03451c5548679912243ca8aa981884c842df6c979eb2f1a4a05c75fbe5b34d7c1facc4aa25186b64f5df99f5ee6024354a5b380f9254dc04a5eecda6bf24d2f9273288cefb9ad935042a7a520d4287aa2b08ab4259963cd6bd6ba796d1241d171b319f6df4e7053a043262c7c1487fa67ef2df54caaef8f797fad6463d511e1124d77f361cd948081c4dee630f8176b5a66bc5cbda8c73765f473e53255631fb670d2cc1fd4fb3cfd9e43459dd041f38e57b2747a1bd63fa804b8f0228f696443343ea2ff2ae7044a08ff902c60cf9912dee8536ccbe5c9ed704021496ccc599f8dd937e27fd8117722e96f748313d8645072570ee8190af7609d6138b89889efa4ce9ac473eaf399b1586b8708b9745586d8af6198f5eb802d260291656bde1b9f45d04e5862b068604bca889684cd5be62471c720f71c2ae429c21f0b9fac631f0d13fc4f994c7b3945e4ef27329b43d560d6e267efa370d16aaa25c434da3519ead94f6a34a074857e04853db4e293f67f43a987c22754cda275afe7c24bf7eb2d915fbff09aafdb0467c2c1caad2265a95bbbd59501a34f5f3208aa50736b92833cbafb334a5993a6f6b48145a5803e61405d7129d34cb3d75a560172210a9f4c757d94e3cad3a5dc35c12f26c6cb52ee1ee176f002f82ade16450084c4a925d32c5ac7783253653d9b09013ad9a2820a8e10c576b8c6716c626788b750567e39452d09107a5033393173154168b0a840a4715d6ef42baad5687455680b26972a5d7cc071c5be634a8492ae2aa56fd03f6c594e660020507b92f8155cec3bacbeff2e995b14db7808a123d7b42c6ef6752af3a5992f972beb2e255138a42a1976c8f0b100bfaaf9e56b701f912a4c36e6173cffc73b60575d008cffa0e6188d18cf2df2e5a995b1049272e8495b8ff0921d441b80d345b7dad292278321ecc34a2666932cd4c573db3df7b164215641ab119925dc106e76a875f08c855c4d068efcc1e8d273275840c184d11ccbf97af5aeae0d5f54380f3b2bdd9e10d659db41f75125e31474ec4a3974a6884a06194a59869c6bd596375d8ca5bf1471fa2b0e1e85333b8be56762d50db73c9829f1a74cc94a818eee32f5c2b82bf4b7c5a4f540f7448f30e873e615d2f945f0581c64376a7366e816e7c729130b0ae7c3191a22ea66da9b996892f2a5d5767c1fc1c04a6a35ff49a0118bb63d9a58b5daa2f7ff93f7759dc2d6ebf07d8fdbf482358c9a22924c30699ead34fabbd2cf10bc86d56414063e5e0139c10dbea00c2a7a70f5acebf8e6887ab9458262c40b33ce0df0f0625c546e9fb717b115521e7b95e03b34d0d9ad612f3dee54469225573089bff802b4d2b91242f3f4475daf44c26ac639b7c5b9cb4e6f28fe852578d99659b4591d101cbd77f1126b0328d9117c122c8eaba5fe18e9af45226120126c22d12a317b108c1438df61efaa1ee75c0f5d30902df4580a56731812d577706d806bf51ab098994485600835b85de4d255ee9d3b741500393a9b803e8cfda5be51fc673023522dbea258a3bd03d8e1d3b9c03ea4621deb203b53b64e358332cafc06de882dee49304e1fb5dd14a51eec0591a8104943a717fe8680054ef38eda5befaaee3d90e9a6f66c5a0b785cd119af7c90b1477bd11c9245e95688b8702d0a3617938a9f6f8e1028edbb193cbe6680b870e0f78e9f606582559bdd74bf6ed68ed39c3205937710f3a5d710022813e1d1ebbeeb8a86b05c067c7d92bad82d5175847c15732ce281aadbda87639df5c5c9af7aa358c8701be66a32b268276f35861551cc1ca518f5effb1faa754eca9ff2b9a8cf0d9c359af9a2ecceafb107f099e8c34ebfdd96af4ff72d53c60befcb8348d2f4345639e781e13ab6906a47df66090af9f48dd625cc90603c3f88d092eadaf1932728cc426b8138ba7a22d0269b92adf3f601de5ad2775f2d31ce48b0272661111efd3dbcb90720077407ed523519045af214f765ba23cf2de6f789aa3df2193579da75f84e533143ba40a94acbad978ab6389a33cabef8b328a173283f6b55d8d3dadd05087a7286a84a748fb259a21b63836097bafc2204a3179d851f7a8d2d9db7e6fddf0ee809550d881d8c3dcd25a4351d2faa58fd5a361b927fed995d8316fd44d876b9828d74b1d763ed5b4db1f077cde1b4b9b1274b517a81659da21ab200acbd849b20a2251b10a11b63bb012fd0c495ed243cb875ae7f5f320183120fc502f7961fe0d8eb13fa335a23707774173723e9a8f42acdaf6056116bdb59db0c92b24a1d2689df15cd231cd01cb99e7f2766c9981b598908f010bff0857fcde68a9302ccc50914e3d0951cf81d01d12ba3b5695635c2e8bba7971d472374bc04ab3c3f9bc143e99639b4047729716ab3a584ceab3d3a44a201b1d75cb5854400824950da8fb9e8ad21b68272fcb5b48b843802a802256cbbd3e5d1907229fee28ed6332b0a20485c7bcb40cfd5c64a25792b55b77df62e0fb843db747b6a7e1fd89c751b7b03accdcb0a226db0d7c0f34c2c1eea2ca6f4fe1ef9abe6c5a82ea970222719979b06f4eb2a1c94fbf8c1290fec0bb7af0484f9e01715bc47fd019c8842a435b0b34337773ebfb0d283cf16c801a210274c694b2ceba3ed51a787850b1fa8116e39281df84644793a9b7333c9419bf3ca063c144f72c4d465dff5355f6e50c610b5c3e0e0fdbfe55dca2f42bec858917b18500b432695789c212405a4e8e0b40b42d45fd6e7babd218b6cba15cc8aedaa1531b59ae0987817de091aa9c5b34130ef60ffd940cff3b5cfd9ba51b4608af93d0aa44ea903354f2f28fe3dc233201255f6bfec854eba07488df5a36c05afee92f90d838fc4f86bff81b7e970859df9e728111b98a07d8ffb190935969e3a5da14ae5cb7a0c08a9e1e92c5ed0b643f77d1e9639321a3822a285af5a512e6efeae763af9829dd32b8ae89a124eeb32dcdefa14637894635321a6f9b4ae80aefb143af639f390b64c745103db05ad4d081235e84faba2d223fd275fb5f6308473ee168bdd368a9a9b2f559e0c1f1142f284b57c1824ec8713b342c35acff283bef84fff191f2deeb9626bf79a3b1b9e542047c527ff76f4a9b699b4d4ef5333049c803604b711625a57fb770993c0222bf1de0887d67a55c6db5dd9bd00cd3e5013f11667e215b221c909f863c6f61e1b0e457d3293ac98af912f0a71b88df2288481d4e420a48a44bc3094be67fd7e16312dfb71cef395588ffddba7c64ee110aae425864f45605ba443a69d89b9773f61549f195b720913f44aece8aacaa0bb0e65f7cc2487102604fe1901f9ba67e87a5165c134f5f7e1447198519eae8edf0d5799d33dbd58fd66a1fb3d533c908ea32f319f77eb95fe09fbe96895be8f877472bf6dc305eaf1532853ff2ef46e30b72c71a465f3c4745f275a984886cab0bf64e87ed55a581224de597fcf4b02396570d3478373d77d499e7972f7b0d6cef35ffd3b3f6c8dd3e32bbc22979610a1981d3576084a52632a3df66fde61a71bca36f948d90b14c2222a876d30154e918647dfbb2d0dfd3c1b8fdec8a2f2fff36484f461436c918e9f7279711dd5f28dd30e2577c0b111f3f3b18181c4a1a29f3b2c27dea1acd19114e4345172547b37a8df37932a6ff085d81721d2365eca72ea4b4f12253ebecb09d8879851786fd160348116a6fd23167813e6e63b8e2e08016531697627a882b44f9314adf200d6d0047eb5d225f1855d723f29f427622295cf78d2c5e12234cf94f4e0540e32ec3641da4d1e2025a3cf9a187885707aef248e65682525ea8963a61441d158345105b6df7c0587819f215afb122318114617a3f253e634b8b082c0bd82b080c74e755d3cbe9b7a51571aa5d6d2c509718896fc1d42e6df6e6676b7cf745d6c91a380f360b0756ec9ed3c52450642d9e8e2104ed6d6af13245756418854aedc51ca6e2e26576db72b7ef7db598fb72b86f3a52c85e0b33b910b2bfe041528e98c91df0441358fa28b1de7cdfcbfe5edbc757db57ed97b170a8e0c6686a6b17f09865e8ea3099fba61f8ad2a7e03b9651103d8ce22fa258515ec66599a1f25a85cd23e826a6c6de64f6857a93160254dfa2c004dee60b7442ebce2567d76058ed2b1dd3b1e96a156bf52c8392aa68284b38a895734f487ca58856ef5bf1e07208abe0fdb33e0764c86f22d675a0cf8034825b010395c91449fe29d080df432143aa1394223b18da6c3dfb54bc0b9a24e45246c2608f8583224c857bbfc5c1d796d7d70531d2d61027fdb17a476290a2195478bfe001e2192392ddc6a444e58379cf2508cd6d4510d90aabb5d7e09e3b4215ff5a707a20c571d61c06cca40e9fc8334dc357781350c37b79410e3598452bbc74f54e54920cdb1bd012ccd27ae4b79797a8989cd74cee9732367ffdbeb613228213530bf74ecdad8514a7b90328e4f90ad40989baaade78e041971baab8f932ead6c0e8cb15cf0f6002fd66c931ba45f393f82c83db1f08e8b9b53ef12cb3d86fe1ecd282d9678af674b4bf1a2f2d483ee117366d241d622e03476bbbe00515c91c0b51d7c87f433f8a839de49ccfd7d1b893cf059c7374eb9a2f9009ded57a0f2ca0a881a5f715dfc01833cd5fd3521709a2b8fc177deee595b3ccc5dbcb0cda87dac92721765734d515a88a100ee689438cf958767fb0deb5009b6bf181262498c4ff0275d715d21a18f88396661d5f65e0465185b1c257c033baf558605249894060e121af50f089b703e5cb04625667d74d9990de7f048e336de01f143fd7770bb6bcb4d64440beafdb052cf158d8dbfd9b2f559b65cb411ca27521b1ebc5321f48c8644111d9f8b0a8d1ee69296d8eef8e56d6ad74369382bf445ccf8e8f636d9b5c84b3a960b6a2d605319dcf5fb88cc6ff18936d64821aed75cdeb30305c9551b5d8aab487bd949dbadd5b49ee2790a8c0d2ba41b64aadaeed6af484c15db3981006994a0f7a35e4f130ab5a6a47d31c302c1bb6c9ffe32f84405d2c1196df855d54fbc65ed3b3f556faf2649b5f87491d5570df073de32887544332b80052e2bc332cb88c251f8c2d858757175302c76a1a4c9a204e74fb20a058cb874b0cc688e52d35176b55a1eb3b8de03865c512757fb04a3d940077f2d820ee9e93e84c4651d23537bd3617561931765dbef6100890522d267edc0b5668a0291edf11279920dea52bf3b12ae4f4c63c0ca214c7c513ec4578978856dfb42d5b06bb41de8cc2f8a3a12a2976aadbfeb915bd81806894b7433b9df1874efcc0782bf2ab9dd1740ee618b709a8992755ca70b5a01820c9255a94adf56758147e7a47115ded989ce4094551ba24f6bbaa43e675c4b4a3eec58a74fcb82cee7eb391db0163c5df78665bcdd7e3c6ab440719811ec2d392714c26f688330632f57c2869277427643361e7037eab7268115cb30cdc47c4d6828317eab207b5ab6507549336e8385c565a7e5ef49ea17607489d94c16ac3b4e2ad7e2c05e847b64d4d0057682c82d87f6bff68215f80add18a7d375f92655ff36850163a7d0f11c132c39e968be422d112507298ad17577a123902f02b3e1d3f07d7c2120257e895e92f92c56a0b48858b33ad47d074ecb07e4a9d1c96d88eff41765f9fe517fc5871d6ede065b2aae2e1ee0f3ff799430758cf52afec259b877eaca373477dd65ba948f26b888089fff8fb5237da1a6232b54e65990d16d72752a0d3db900f2b6ae935981ce60a37fe784c25071ef6b6e1cb70f868aef1b5db93ba4d3da7627a7d598e6c0382136008bd2b281bcaeee2f1ca3e862dafc70412c84abac805a22c536d1696940e6e461048f1cec0e7520aacdf295a3314dad7c01ff7f984a63b3dbaff67d2708aec10701476d38fcd0643ad82b43e7fecf30e16a1df8457724894cb1facf54fa61cb7508bb47e8829d9b48e33e502a4d77846630dc16bb41d075cb9f88493be4fcf4685b922735790e73d44276a568ef134996da2c33db8455451d75144c9b62e4940184fd9774c331bd1117ed0a24e479fb23cd0ac28e6b8c1f3d71157d3eeb100c4ef6f24b6c4608b25b8f6bfdc3f23a6f7ff06e846ae7946fea4759962038f327d5dab09174399e0b6b10d24527298064d24f1792442f8b2f1616522bea058dba08e39f5fca419e63ed975d6bdd3163fa38f0b873dd0177704921481afccdfddfed52e20209bb947a7c5b5eb1ed2efdb8ff5f9d97969c67f18f88ba1985cde4d7f27156d5e1a8a1180a95912d4fd3db96efcc9b26a5a4ab4a70290b2f76ec030cd4db9ff93a2a4aad7dd9061e03241346020e52dfafcc72483ada76cd8ead985dea3aa7db21a5a30ade6bda02c255a037bbbfd8fd0713bf6874262035d1eb6dca4419750e214e848f02965ce745d893734faa3bdd2640f8e1603d946beb2ea9a0368bc5c296707fbe4fb482843b1392c1293317db7cb90209b8fa7096274a8ba00437b5230248e9c90ae3d7a1ece46069e923b151e7477a99c8e6443335a7d9f8df108974df68767d4c427e0a7c158ede2986824e69207d46b1bfbad152b5c8d47958712c302bbe8680980c72cb7d16ce6e5d750338da061d1ddf93ff752537e2a4d915ba683c3acf5555a1748f41dc03835de52cf3f19af54cf14165fc77db3fe9e60f17e72a71bd86a5840a8e64d6ca47158095795442a8df291e71b0de84a4385409a61a6e67d98f1e1083ca34f4a0023006970c0cdf133df147b20d526e85d5754c54c2f46e0d539cb40c7cfd4389a923fbe3d09c693008f2ce226b1f71460245ca9c7d00e494d5b2eadfd0b99a5f55b25b59d79482005c0749f61df353cc1eee96f77cbccaa8fb786841cfe78b15b958cb7602b005b955daee806bef2c919881bd57107dc727b1db98e94213bb31925fb59b9c414a450b309a09d2d515b0a54dda5c964d5aa02728f774bfb6580b42835d16204a46ba6fdbaf0151bfe4427a9e1d33b9ec5a3c675649836392fd69028d2dfdafe920043c809e36e7b79cec0d193cf27b3e45e31a79c03ea0caacdfa3e4e13b77b45c4483db878282463eb4530d842a2c46cf86489c1345fd85ee4fc196b96e4a38dead0009e0e99e57947dccdd0202a73fc741f59278582d78fcfa5d4abaf4bb3f8a3b7d62303759c0a2edab50a2871a808fdaa0c7e3371fc3b7ba8db87e9f4cce8869e5064a8f0aa9bf66e4da4e8874cef5ee1603dca6f24d5a5a5c1d8877e76e9e26539e2aa6b7631f256cc3677e4283d9fdee7154093a62dbb953b5d751cb7e4836348b2589d09a23d6a487a1d7679a722922577fde5c0342c6105ec4f579cf5ff17340e84a506fc6343506ae6d618b6eac04e9527b1a0b2ccdaea04ae4e6a392022010be60ef6d29eb4506a7b46f13dd6debcb69d95542e0d4eed9f084512d413149d2e3163104a57a6516e306faff7c76ad5fd67f7999f7ec22d1ee0ef06c3d159b675be9fc6feede9a14ec9f745bdd887cfe9923452277c1f0238f7a99fa61715e1dab8a3f39a4cb65a75afad65018ad8fb493a977617e4d3b34f6649dc71722f5ca458c57dfadc5d5ac426aefa5976d2c5da5092a419d438b435c5aa032d559fcecdababe0d28af06b1f84ab8c2f0b5e2e59310c6df57dc074811339958c2c5196a234083edb462f7824569ec7525f972c235a548453a24e012e2937bd2dff4a343ccd8d82a5b5a48654880fc3cf1e2252ef0f809be282d1431cd1ea8329053fdaa131133f2079b0e2f9fc6abdb114daa637a055bedcc959afbe6d3d2abf609ecccc1c5938ec10d71c24091e5ab8a1cbe830ac7c28532d5eef059192f6d04da7cf38004360501759e6c3c9480ecd955f8f190c770e8d57103c5dc718d989764aa07d85f4b995a0131b647baef2aaed5e833a658c311b88b2033e77b2b7aa7131ade2ff519fb4b411c439f86029e21517bfb811698796b219b8f91819e6745db5f09f1205caca2d6a75da5e41ebfdf28b363cf4f2eaab12cb2d67fb83eb221b455a3785d32b556742071f179e08c39aa0ba7e9be8b12dc30910e3ebbdc0e1e401c6c7177b8e2b7412dae3ab2a2c42f7161881b3452a3e74a48924c7474b0042df00a91df8a56201de8b4b02ebd1e389526b08557cf8bced96f5a46fb2d54150173c1ac5eb6a3eae6364f51d21976120f7c9e730301124f9bc502df8b4ce2ebed9b0d3c1fc7ed70d3c08c13bb60694e567decaecb721837cf41ff3b8a5d01f7d95670dd3deec2bb5fec9df8bb7bb61b8f51f931a5981fed69521ffaee66ee36203dcb6c125d25d4737ee06188e5dd6b067bdf405681e26ac350f590a01b515ab955fc91afac05cf0b3a6c1691b38d55dead2f83158c2ff2711d" hexSignature1 = "0e49fe1706d9e93b3793e1057f328c4206632da58c13b1aa3c95158c1a68b8b5d9b4fb001100e13cdac760febf755faeefc44667db4983225c5db714516b867a806f8f883330d5acff28b097f440b75d914b65454169126a7805b6118ad1c7671e28d88aa59d7761c0f92cad83ef4fc21ee97c09a81e5ae20a4475b68935c3ec4d06c1d2a2165505b667b2741a5311d2a78d99cb46163b8b6d65d04ee40c2d3e2289b0d981a5813fcb4b3f3fc6b11230fd4cffd3b910d7f766ddc09e95d3dd6fe7232b5e9cd52d09b87a8025e9841d2fc2b890fe0a0f3c4c2c8ee7b464f4e1fa0c0b8f4c57d0a3c94604ba22fa6cba188d776bf9e9e9e6471c61b8ccfba14fd9489fe795c8afde07ecaa802f05f1cb6945558d8840321302ff4d536e6eb5cfc89dbbf5c9c883587a3ad43770222cbee2dc5ee22f0050d9490a4b1a278206cac70754d4196ea1982eb72df2d285f27d774947a05708003f449cdf19d8aaf8e63d4339b9eca58b2cf9168fbbc27ea6f9b65af1651193370f70e3af4d1cafa7a2db7fd122cc34fc07fea2ecdeb168949e83a3389bb58bce50aff380c707a2b6f66492258e6a8108630e683a51d9bbd15b78c313e5f3b808ffd904ae27a55fc8ae8a21c4e1518267745f3358430e3df11391875e30fbf191648db283dbd98ec0451fab47d94079e2092a0a1879a6516dd0a50c1ec4f4e85545c59526e049a8d5431198a01440cd228c3c1e6120dc73accec5cff2a083caab20729ac2f0008276bb424c796ad7d4ffb9d4b5d213450e81d0a5cc25cb65fb469853ba75f400aea5b7b95882add53b72cf21b27c018938fe705b74a8ae2bcc2c80b0904dbe447968e66c0522c866c31109e6a055fc03a1696e350da0d21ba59fd7d530628fe6ff8ebb9e402216ce5ff3550db5eb38b5e0f7964afcf2c0a9f4b7e4693f90df9c58bcf2170a240489f62ef7ef8196bbfd162e13d8fec57d81ed815569640c9b2f09f9435f1b01380dd6e54408fef2a6644435af3abfd854f4cfc58031e90480ab3d0aa406139812bf64e075b39674d09bc544f1481769bd2088bc04ee2e7e8a7ab910c83ccf4030adff9a0501d2704256c2c12001d2773fefa7b580472eea8b3cd1b289092645293e7c27a5aa0e4de5b95f50cf943bf5b71b9c4a81906407b5d3b6fe59cada698bad7930b1d93e98ead0b5d467f8a41c239c05f715172dabdacce8ce9a663815a53ffb1ba4770b98136adfaea685d5ab01b60acb0aceb21537b701d104f96edc2a60cab5e866c7686c57e1bac8632cac62832894efe0c075e2ae689a86b87ead4432cede9fb6058f80bb6c9d52b2be420d09a2c847fa695a8c8d37047d4df7bc153ca9c955e0485aee8dda93fd2f4eb7910b484fecb1481c82f651d41e4374c128268218fdb52258f257c4caf22f2337c6c62ec1f67c423e19a61687325897f25e249a3973456198ed516c52f17d306146c2fbb6822e5650e6212a19984d388718a745fe87bb127c0a4b390bb8c865513d2cfc0063c38a41f4aa01591901009a6c0b0354c42ed55a6da166d7c109c676779e83a2dfab81392cd55cf1262b7782562a455606713df9ec9af4bac25c9b8854c15a4423062847691c691bb63082c0ac79c9e7e00a0e06e850128996bf24ce742c8c85c939ae497ad0d1d4a9f7e51db85b22857f5690c4b833439109d4effc166dd522dc0d2bcea10d401248d764a37cf39cf08e86bd71accce49b233789f54b4b891c2214da4d1778d4d26955dc7535aa3b5cf4f71c63f654c4a16e800946590aadde41356f98b9c6b98940b56b2933830e0556da48de6f71766a4a7722776342af68edfc0938d8eb825a8f82042cd0f915357e4c8c1b86fe92398382634d54d2dc93c42891dc8b193f9790fe54ee58537157d29af2bf1c1c6b667c365b1768079053dd1a9a4617a511ab7f4008548022542ffdf697e78d93240bd63d13977cdf39ef936bf0f8a464dc7f9fb2b742f61f1a7ddcc88a83d96ca7c65b094c49af7099c084bc53a2f20f88f9cf10565839f8ecee01ade85508634a27e58bca4c4d224b26a4c9b897514254ab0f1e297f48d063804d4dd60f63d08db985c0bf8852f8d8296eb92f2adb3e607bde0a9d9ede0d8328769ed2b6fb3c00db50c2539a034b7c960da37eb6717fb3c71cc47aa25fdd9b27c8ab24455c9dfa934267d101de0197480a2820b5c59597930675a1d3269f7c3e033cb41dc08001dec66224a3d2364f6f14b3991ca4726907a5a12f8f5701b08c9e916523dccd74c0e6bfaf55fcf544c3a22eee9415c15113d1db15c9379f9f481e4a427d5b9bdc3f2e0ab5288a1f0b46e8e4c4af023f65c4d7ca13f0ec4bdd50821f90ab8806f5d33689de6aad54c4d9a8236023635164b6400199218e3dfb4ee7aa179987b1d4e4893a190962a881db6b91a0c3268f6354e9a0ae04924667f65b834ce30067c988019ff86984bbddeb211b3d942f1eaab6d213fec08c7ab30842ba11221ff29044e4e9660f0796b437d3d0d8fe9e2fc60e67db187109b7aa35b30eb38fd6252b35d2f5c3cdaf7e1f2311ea6ce6a6de6d1b681ede35078ac0a08cb79f74ba2fadd768b35ab789f83b9f9a5059e500de88a401d8a612e7740ff9aec5d1bf78e1cdd485e01b5f45da6e409b1372fa0b52e3918c6952c0f4058357aeee776b1eb0fe31615ae1a995dac73fc84068233b5b3d4d2c04b475a293c7ccfb6a2a6eb6e080a95e3ff88db38b60ba8127cd04abdd9055ba27cc8e878ad05df4b22f92cd5858864fbb7edb8442d1045b53374f24a6a233a1b9193c552c3c845457734f8486352a3b9b11b513ac232f66050b8720af60aadff5b2edd1ab0ac51e5f46eb1cbcec86e0a3acab54fda2eaf244590390c4a16367274bcdf1edecccd71af0bdf568bc6a2a143c0323f6e16d24e4e4ef647483a73c2358579e1ecdad38ce68ed42f2f86d2fbe8f3cddd60b07a21d9099c6b08f22b97e1a022af285740932927bc88c834dfc33036ce86f095f5bba0bc4cb37d330acb59d6570a8e28297952cf4ed05134678605e420db31471b08cd6e4471637b8437f8880266f3dcfab80c22dc93b842abc923fbb12643f3ad3c68ec2ded37c1dba3eff45c9106db11535b14da39fc268f127d06a6a4da2c20090db3fb4cf35bcc74aa8627240b9fd9964939f984332217bcc00b579e87c912114ecaa2180b6d3c3a42c3bfe24fe4e0795fc16e166fed94f3655932a6c3e2fa1d44e2f9a95e16a744fc5c15ce84abea3d040f77404c9ed1630f27cc11d1f885e56bd94703869989bfbd5247b0cb67243061a827a575b6958ba2ed809c55d75f898eb3074de5e490f43dcea4bd1e8019683b07f7270b3610e57837d57fb6a84e06ffedf589744b84e190a8e6407440d53c9ef7a50d179e2e273d35e37529b8ee1b3f0227f91cb1889cd25601fe685b858710da059cdfa4e84bd125529f866a349a22deda060d4a1eb59100fdf22a1ffc1324473596f988a372273d8f593ed3477f2cf4d035f8e5c46e91a6067e270d84d6246df36b516bf49979c37f8e7eaf8f38bc173685c76db015af86b5cd34825dcf70d3b54efa55e9163f25c1de29c1ff2a2a84ed008722c913343e727b6812216f4d0e0212359d5df3761129b4a95d8d7405e34017f60516d4c675df3f08a00e62f03010fc9d049ccf8b0a676c6f1603959e1202a6319706fcd28ea42d691283cafd925fe7cad83ded6f8e79ffea0704e84850d6f1d695e1cdbd335e11ac75abfc6ce977b28688242fef27de931c52732b6eb1d39020f49f5c5cf48878896de5d412435c79a810a5ccff084c97e441ffa159170347b72d67abb6f15b14def092b67083b0a9ab5ba31c9490e19266cd029c4b91f8c49365ae03a9494463a23a9effbedaf2c11592724098e357e50a2cbe3a1523fae9a7a045c773a578fd59c413d664d66a9cbb5927a237360184a0e0e36f0c274948a12c2aebfca648ddc0dee4164975b4cb0e5cfe686086197aabc88d6daa70fe992d48b86b224a8b123daec21352c2bf4b08664e5b90fe20dc7b378767306e9e86f570c82d53d025babbc546fc7094c067da9836e189140bd43734ef9834be3e25e235dd9a3a306045ac98a244c25c472dbc226505c8cd8da89f68641ec3932a18b720bbda0d072bbae4ae404cf4b8bb1f17f2eeee0a15cd61d53775909b0d1eb879422b87069afa96bbf82b938312b8231b9f29191c4c5983e3f596984494f3ac9c8bdf17316967be9ba0a9ebb299c17a9f411d9fb0f89385b0f5f1a2fa863b07a9a556c3485601ab9e775638c09ee62ac56321b213cca5376cb77d2ae0ee9d188c77d7732fadb726965921212b47be2619a7800fd2431ad104bab4b0ffef4b657c03e0cb96a3931440ab20c977e8be1186b090dfcd054abb01018aef8047dd1d5b1fb046240cc7c8d03f8cdf1246708b03b4d0f4b4b1f2fa5c40c7b9c8d680b65727059503754c136e4491c4043babe09c5d0a54cd5ca94553103ebdce73650677096271f573e7ca3d9ab7189ed95808e934e94b7047a22c8d070ced1a2748176b4d1eba8486eef5b2ffcdf76c944cee8a9afdd7dc7c97aa3782cf8e87019c27b6d69e7cabda0afbc60d16df6c446bc84dad548c698caf8671c30e1480ad5ac40c1bcfe9a16b7240b59dc34b5a56b4a619f348727b1f215ee0943b90257cf776b1ed1ede3daf2bd1c1a70bd6236caf450b5172542fb1dfa9a0f15e7ea9de63c516a5ed937b2a6cf53db75817127c893184b7e2f2e12bad7b726ce8f06493e3c6324602c149228c93c99b6333521055908e91d311b84cbe4bcdc333dabd3592ec8b988a34b6bd7cc92dae7915096e9644176c84c5e6709edfec3404337e37fe11ce676bd2aa1a4e9a58d025142875bd52f8359434f6cddf05450ce870997f038a80b3eb1c0d8f0e469e2d08e9ae6f9cf0bb61cfdf18f332bc035ce8452435649f54fc83bd912be0651670dc0ce4baafd1ad4fcc9f5290b8f2ba0ff44696c9132dd52eac26b95931c06b2be6afa51e1d7602a3827a8a6f074fbbc2492f752eb8a59ba96365e39f281a22a59099e25a2fe6e3371747d733f9865a6509078ab2528bf74b196652e3fc5d884a0d198d02d51a60f705615679edc6cda6ddba41afacff5a776db11f72f6ab8ad6d9a9c936615cf37c349e627dc3e91bf354a08b84448234fa2ec9c6d54cd9738d9dcbf1c8b91a3c2c2d912576e4e6e35cdf35c092202f772e7c33895f64fa899d2280f5206759029df5cbaefc2b61e40bd148c611a6a21728345b40bf6a2475bced235e48a34907fa47edbca39e8f9a0e0ece57b6bea14efe79649d6a538fd5114163b5db44ff90e7fbd493ee902b6c275a9cddc129bf8e99b74c7f26aa8114d0f2c65188c2e913c003669b26ab3f22d4082198f9484772a4f3eb8f9d9d2936926642a413577c8a472c611a60d55bc30d2a7b74eb57f95ed836585845b68fd5bb145ea20350d6e06535778aa7d606a924a3c9619a4501015f7bfc720f0eac1b60592f1deebf47d5ac143865323931cc470fea2e060379629bc341c59e071e3ae145a93a49838e4938f4f60673893da07a52407eb4de538bfee99de63a85ce06db231c77dfd033b974513938644b7fd19f2bed75a4da4a5f3ce71834624b2b307494598b39dcac86b7642f02bf25559915cd42ed57180bee8cc4323eedd3a4c40720c1a07c940472d386e637f6003e2252d1310a71f0ec0d3d43016ba2e1a102abcac4238aea9ec272c8d8b91aacd87802874795164c0d14bc5bb0bf1c67179620d45f11151c74a6b3c872a5f2690f6344c0214f2e0560ffc7aa7f7dd067f4d7b4478a2db3430aadb8ebde065c0de928d57509368ef3ac33a116bf34fe364b188159ff9c72177f61844807c331da92fa7eb0b6308c1705b60106235c27c49385e6e750ae9323c39ef2ef9f0eef7a653e8115367d9769518837538a8b809ef42b04b9dceba2a88d5a20e2a2b23b8288057e9e7034ee34f1e8bc8f90b3edd5c0e73f9140d1802ac19cb0ec060d2c0670e116886b4bfd0bfbd8ec2b525abd01e4446093f9c2064581b0736cf28c93d5cd11d1f80ab7d64faed8a99f5ad8038c086df5a3c34f9909f4b0740bbfbe446cb84df3b5bd0ba2ec5901a74f20f22893167be220a7a304c1cff622d8b606bd3068f7c01adc39fd127d97acd6474bfbd4f0dc87e71a3ecedf7290041f0c591547a63ba0b4267c1e6ef7c679d077dac782f235d0460ba8da668837008eb7006865a4c50a09130e27d26aab97c9c4997576083cdcc1c524ac4fc9743e739c2dd6b8eb41d3e6a801fb4c6113ee32e0da6dc2aa94cdc4bad2f32892f33505cffef9597b2ca3720c983cc4ce21df1c9250d3bb3a4cab2131a836be7ae46f89ca2749be1a23cabac7fae8c70655d955f9d2bfc62156ecce51e3a54cbb5667387cbf8ba3b4b15e4f8032944ba0638df2e0dda6713824282fcaf4684c49b2081fdc82a707a93bd62a1c679c22199e5c1253d9542c7761467bc815e8f72c4fd870341fd028e0a558df0fd49a8841aec8255c6de16088a74018a8ce2e5521f262117179c9e71300f789164581f2bf33f9a618d9245b5afbfc2e0be97a2fb86e2f228289d191544c286f2ccaeffe54263c41af856699643b225b0574a6addb63cc6c9d6607fc4dfe4af97630873b6bde63c2ac39c0f2a82a1040ed69cc759414bfa67791ba6b477b2e9e9114ac23c5aa921f27d327089e183477dfd0fe89c61204b423e95db8011c43b4e517dc84d11691cb815530b972cb8d6846d0ebdd04dd74bec66404854b21d95ca506240caf063b20b3c178a8745d42f9dbdd5ab8af3c711350f70bbc33067472e24220071d05aea31db665cb4c3ba0f26eae37634f9b19e91854687e9262ee86f4a0662716f4d4ce07fd57c0bd79313eb8eeb1a8db89a545b29f862abdff05175df4601c0ce84f4e33e85afea12c95de543eece7368d445f0f48165c4420e760f8cd1e84e25710c7717e657bc138f432c3ba856c9a243f6388bd0ca6bf324ce41fe161f05a93e0bffc2cd353fa4edd4f100fab04e6054687ea1623859b5238f16b0456f03b06d911b120b8499fb65dc57ca7e253f41ee9147f96c35d318dcc50cc766b6d6e2a48e0849051bcc5b2bdf65f060218a0fd8aa3634315ffd1f49473d06e6391aa358279a559935b58aed7c03037349af452fcf230dc2c385c48cf667d6681141dbe584a7092745699cfa499de75538dd0f742e97b9ec0e1fe7f6de187216707865239cd7f9b175fdeecf5a49d97e3e9fa8209a3bba26e59fd8605402ab6b21cc5aa68b2ad69be80419c53e81542c12a3458b6f90c60c2e25d0ec44b87ecf7e10da4bbbe6a115db4ee52abbc81e54204c5bf2ff9c52db92c908cd41551c4ba882c864f9d4128963b3cf41504023735348fc232495d24ea23c539b635f536ed2973ef9854d19b73cf83c0e5bc35cafddfdca42590a8d3268b11e12d74fd1065e901f86789fa6fb73c1d4575520656eb7bbf5ca80b27fed31dd526e61ec0e40810358b70b295aed8d08a03bccdd84ff5c3409a1f23a9278600fee178a97acf5c0ecb81ff68d27ab3ff0676dff99ab77313457e44792de597e7d6df62addfff4a341eabf9926ebbfb55b5a7b3118aa3730d48334ebec98dcf8c243c283d1a726407fc70ffc0eb268fe4409167da69df498ea17c9e35cdeaaad0033cb18ed62cc1dc5cbcd13d076aad20110b636032ccb575134feda04401f09b05f686aa3bfad9949cb775ed4c23e6065044cad620c50d3d5d6229a569ea1004391c9c62882c33e448077266fa9d461d71040abf778a0345d22c9745b4b266e9fae6930c92217bd41d966d1d391a0009d336ab8864ad4ec5c727f05e44d039e7e4fa29e5c6e46340cc37bd380b564333cd4891a00c5990b3782c86ac745b6292f6623dbd2fb730bb7aa518564125c56ee126e83f3538c98c453b8a87c79cf4285f8377ea8469806c3f99fde2d4225510724d9fdbc6d9d41b01ff70804e5b8e6286d5a0135e256d8725eb32b95c1582f0f296fe3296453e4407d72d474d459bfa4d3a2852e1289f8a7d1a04ef42c14704b577be4448454dfb08ec645cef675f81b1d3547497615508121fea69085017f43874ece7c54b6e5bfc6307a5231cafe2f5191ae5c136876427542f73439cf2aa69f521c69cab2ec89bcd6a290c594de1d951396d36f9162daabd3c789ffaf8527c39dd66697a1592ab8af20dd9b0da37295615bc0ec7fec67825878abbef553c04c384ba67508e444d60a95969a13b952b6a7e890f9becb83a09f097c40cc8813adcc055264024b327386e3ff6d942c2fc43325da26110af27cb8e701bb943920da9345d0f2c80464152a32ea0422f6647ee48a0e7522bfd32471f6e6f455078428eab0f9eff43afa68d92bce3afd5dd0ec87ffa63d7d079213f8ade4538454a9363fe9082dc4ee98fbc2caea6f686775ef35f450e8e0b335f93bdbdfa0ee2b2481ad33c3e1d514ee029ba6ba9e5e8bdb746bd3bc376e201bdd6d1b3a9b6cb53da23262087a7ed64f6bde2b73c2dc3c538a94c3b9a3a163ac85a181c7e8866fc19052474076a8a6a6af50fed2065554619f2fc0a38653d941d9fb4df3b74768bfe3d5a4be245ad7a8a002a62edb0d514e08aa3af1626e963e0217c1f2657c4908a49db4414c3e07c086309a2e4676ba79d310f283a38c1f14534ff39e6c744ea72ddec43b0107a8604b5f34bc9a31f200befd27a9bee1091e2deb2f8f089aa1bc76f4b09fd21886062959db554d28f328cc5e1d2ce22f055b1d105a52336d0e7b8e6e26c3d872d9ca713f51196e2e0cd1d1c33dd933115579c484097dd4351645aa1fe004a9c8b42a2a3fe4a4140c0af8686eaa68f5dbcb5a14a35cb91213c30531f6113adc5b5b48483f9239a8f8686d875bb06d190237fa3d92928c9455d21b15c0dd801e07141e300b6e116d39a4ad00b698f39092e3520f813a68b56263cc7012175c1d1ce708d8969773ac35a175f1679f03b248a38ed8519d1b48d8ea55ca82dec86b0c32235c689748e29adf0ffe8e04dc823a67a1bd220b98493e91bd614661b3673f146c68b3062c78e9c839701319f179f36d682610ef7fd96df532b48dfd93ae2df01bb8e67360e1c3b08caa8674af3d01cc044a8da821ff2e3b8e595e7ac4c16330e00dd0ebac867feeb40c8080e2a02a42517fde15fae36c6fee9e3afdd4ff3dd9b8e0c8927b0ccf47fd2cd4a421d239143e0f0b0058159175230720d8e954e45b91c83ff48269291e678fe705fa95e6f49d5eed7b32f60a66bd2a5961afe862e40acfaf45c56ed39250b49cc152c33e90bad4bb9d3caddd0bc9cb0d39cabea9627691055c1f226a905922ef23ee145029258a2c18d54b77b91efe9e560b38d3544812b8a40909dba0d18ce662dcf3db27e9fb38b056976efad122110b45ab87c0ce9130581cc2026fb8713d08ba9605a97c2a6956d906af1b41d5bf77581815ea61873692d32d2f35a5fc1db551659dfd241cdb75d8167658d8663519c05d3446280c059b9fe898ecbadbf763e346b469a22ad95fcbd67cd50faecacccb346aa5ebeb05d79a91f796a2e1b93539fa7a45aea2435fc301ffeb0ca21220b0fcb62c0cb48f1d113034a471a8bad131e3701176957f828aec5a0eb0fcd96028ea3f39d464560a891fc0303c2f0ffb17c384e640c5ac0286ad10ed83f8ec310a886e501d8a88f12b2aaa3e12063a9801ca75278756f316a1cbd21b1e41994f7ed5dce24388f210a2887d103abb1a13456bd8668bd924a3b30abb678b9816b82af7e956b64f5556b72c38b3cb1ce5a83e5952b18f42e34e561a90daa9072a908cea034bbc8708c57d7cab14fb53ea24dfa16f895c3b34b46875f8744b159d77ac59588cb85d55a07114ec002d14e5a9e7769dd75fbd52b36ded9d6391111ee24c8aea92b1d6934efe840cf65258652ee471ad64a648c286775cb5d1bccea27dcd5c01c31eae56ccc18b197aa409818c49b961c8920094528f49b8f62d12c1ca37fed2b69b47d9829d182c1ab6bfa01ae420921873da71f13e3f074048e79c0fb0e364752d7ca59d98a8dde86df0bc388b6cfa855ac319f8f801677bf89c060362ec61582d121f4dadfe9c8da63f89a99493f380a611718835b6d3b07663a35a467a4b8c9c2032757dfcd169b0e1b59096286a426e6d1e35f4371ddd415e1d7d1cf101e5d9eaa1be9d0727c764b061f67a304a1279aac6a8dc6613d6195e52e47dd47ae64a56537c8e1551c92c9dd7d13a39b4a358a430f57c91f14c8069717edd89ed3a39a0da23e4650016ec25e005636ebde04e9a2338187bf07f7f204232aafb81c9d4780938b0d969bf8720054abcb8d445d44ec8c2243c4c51cce7a668b7c9adeb4bb306c4cd94cb3fc187caf136a1ab6464428a883185f05e605e4ccdfe04c9262f60e9fb0eeff75881f687e2974e7b978d61aa06b04ff68aff6b591eeaa1f2a8bc49969681d6e9d10d25933c507a9d93a535c3a5e306705b60748c07e07f1093357255cc188463e4fb4c34cf285ef62e969753226e95250828c21b8dc015cb11ecd026e5e32375e03128df4ff32789003e16f88513a3c68abee111bfacb470caaaa31430e0bb5bac07cde029a21fa67e9842d4a6d7964b6d9037f6c000b2c480dc68667c6446d1e050f9e7a43f9df958c28471ed0e33709deeb264091bb83bc7f27c15b231cab7ee92968f269de7838eec01ac9e3e03bc2d20104e3684aaefe2251891b2da54f3354dd08fbc2ba537f35c85e2e5217a6be19ba8aa17cdd2ae81cf202086ca2f06767eaf5cf85fc5060b528b5b6ce6e359ae8e9ce89da320cc8e99cf83776edf3add4ba00ce49cbfbc6d0775bfe728e48934ba99c7d7512799a9fc6b24fba716616a6fca70ea5b039c37d51f1f43a83219c6f1a6a404a3401fdb778dc336b8913c2d89cd1a5b806cf679b915608f4d4dec8de7c3457533f023222440cb7c7b1c3a3a22feb2c639b954238074dea7c9da9fd43b85a3c3a80635da520ab8810251a840143d9a31564912408efe5faa2db3df744d00cb9cd50efcabb609e068306ea9f4b68f1c7ba59d6c4cb65f90a90850dc3dfa09150f1f24f26878292c6015bae8a24fbdf2e4a012736f7d02a0fe11bac9826fb26e012485b476cb006de9680176d16f3ea9c5e73ebe8607f0f77bc40688774698f9be590979678808bdabc8549be9b4beb31a25149080e1b38d7a4dc3408cec5859756e9325283e8b312541a7e703c75b73e40c108fec13413a259f0e3a0f83bcd751bf006328c061ce8efda3bed6a5febd16be95cf955b1463177264aa14c2ef20e2a98b1784a3d82a2b65b5fb0960558b942c7abee33b4c1eae8ec37254224e6118c943e724db9b414312242752de2d9dcbd8475e2d8c3914964c73dc4cceabceb357a2d96790e678451364230425295b7cc3b1b20f3dc92daf4203f1b5bb58e115d2c97f1508db724e4ebb629ebb73dd5e0913a76f2dc16274546d70c1b691a3fae03bc6db9f11cbb7e39756d112443848d51b69c14313cfe276d3" hexSignature2 = "f4f5d9964a5af379594b9872aea6c65b51c28d089e59b307ef53bcb9550ddb27d9b4fb001100e13cdac760febf755faeefc44667db4983225c5db714516b867a557e6eb0210d40649a0242f8602e2def3ce8a9fa2d82439554eb21c5bbd96d13e08cb70083a011d1075b36a09dcba1efe2a36888049c65211229d3ec780f6912c19ff7e1bb8f75c7cc8b8a42e5aa18084ec5ccd44221802598cfa8577e04befa5af2b3bbb2aa44181c012f253b1ef7700114b4e505e4c0a672c7ae8b893eb0560adcc53d5c3adbf424601324d08e61980062e79d7506afdef5b4660e7b7d4700d6c891d95339ed8598f7c4be13dbd5ac404b0a655756ac020aa827612b4145cd96f2b242832c9a0aa14aa957c192f52642e947390c188e0e441c43f458dd1aa289eb0a45c4d6bd0ae47ee2dffe1f03e21e0057907ac06c9afbf6c94933a831cfa2917be304c3c23a9e49574fa2d26786beb9948831cebcc198b2755bad06684c371a61ef01a4e6b684afd53d3aa4f4398506e7d300e2713b05aa7f4818c5490a7fd122cc34fc07fea2ecdeb168949e83a3389bb58bce50aff380c707a2b6f664e92476b46ba85148f4dddb5fa371eb28e50cf2800965c214d74298972cbee99621c4e1518267745f3358430e3df11391875e30fbf191648db283dbd98ec0451f07410cd571fe51bf34ad00d04389013c7652af126d5fdb61f67b1d5ff2bb4e31c714b992cb9a865f7cce66c0d420fbc3d586cc45ae651d3098b96481e69af0fac22cad38db480a711acac3196440d2811ccfbda72551bb9fac7aaa3fb8ceb35690922c9b5b3717c3ec3a97a0aa8d0b42f5a4eaba8f7f2e6312e4f4dd239f76870522c866c31109e6a055fc03a1696e350da0d21ba59fd7d530628fe6ff8ebb9e70959c50215dcedf3039da142d07e5a5b8df4914f5cccce3e96c4ada2d7839ad65491e9bf1019bfca3c4e0cd52b9762011ec92e941c4749d34e3fcd04719535d1b01380dd6e54408fef2a6644435af3abfd854f4cfc58031e90480ab3d0aa406139812bf64e075b39674d09bc544f1481769bd2088bc04ee2e7e8a7ab910c83ccf4030adff9a0501d2704256c2c12001d2773fefa7b580472eea8b3cd1b28909ee7bba83256096d63dacda76d4757a1655098b96b93da7290c1c56607e9028a6da698bad7930b1d93e98ead0b5d467f8a41c239c05f715172dabdacce8ce9a663815a53ffb1ba4770b98136adfaea685d5ab01b60acb0aceb21537b701d104f9af07dc0d97832a56da92394c96df87259da1e201e65bda1383fe1528fb8c98ca7ead4432cede9fb6058f80bb6c9d52b2be420d09a2c847fa695a8c8d37047d4df7bc153ca9c955e0485aee8dda93fd2f4eb7910b484fecb1481c82f651d41e435ee9cbaa9047eb76a33ea0d7b8c71ddc53cf3e75f15d688add87cd3b27eaae2694c01c5b436d8b2ec934f800bbd13b87c5a5c7bbe0a471496953de8c6fc5acaa4372463691f8d0603ed916840677091a8c04f2b5b35e0b2325788777457de896009a6c0b0354c42ed55a6da166d7c109c676779e83a2dfab81392cd55cf1262b55aafa031b7cdea8b2c1506143ca8eb261f52fd87a36b00afd7c7e69e28e33ed5d978a3e1bc2743fcf09dd6c9cf6931e8a53c18f184e12f3b8c10f117f43664ee51db85b22857f5690c4b833439109d4effc166dd522dc0d2bcea10d401248d764a37cf39cf08e86bd71accce49b233789f54b4b891c2214da4d1778d4d26955330c28ae9a061f156b048dcc34683becc0cd9ce5ffbd2968bb4f1445f742212a6b2933830e0556da48de6f71766a4a7722776342af68edfc0938d8eb825a8f82ef5a90a6d68e0f333b0d187eab74cefc1a58ed59e69742ea1cb0b7e4fbe79dca3daa73bc4af1c019d28b59071289a6c4f480141d274ebc0249f3105c61746b2208548022542ffdf697e78d93240bd63d13977cdf39ef936bf0f8a464dc7f9fb289b694ad4543f90b1338dbed53341053916cbb8f645a29524dcb865022db71e6565839f8ecee01ade85508634a27e58bca4c4d224b26a4c9b897514254ab0f1eb5f4b487a5a2e35cc0a7bb111a5c9313e99eec8ae21cadf27f40cdc7cad2c2429d9ede0d8328769ed2b6fb3c00db50c2539a034b7c960da37eb6717fb3c71cc4a12722104fa705b73730a66cb82b2f9512b22dce3cd8337029dd824bbf7dc3037efb28453ad2ef6b37d3e12021a834ca7bde41355661044575593234c76bb99f0e5216d735603faf966e7d44ea86d49fca2e31e613ce0eab7ba2e90c3f33978b15c9379f9f481e4a427d5b9bdc3f2e0ab5288a1f0b46e8e4c4af023f65c4d7ca9f1f7cc59437bfaa2e4dd20611b29a7bab93dfbde99b62575df8cc6a140da24a99218e3dfb4ee7aa179987b1d4e4893a190962a881db6b91a0c3268f6354e9a06d8ff3789b242fc5b699879a9033ffab65ffc6e1bcbf96ee9ef41085b11fc362fec08c7ab30842ba11221ff29044e4e9660f0796b437d3d0d8fe9e2fc60e67db187109b7aa35b30eb38fd6252b35d2f5c3cdaf7e1f2311ea6ce6a6de6d1b681ede35078ac0a08cb79f74ba2fadd768b35ab789f83b9f9a5059e500de88a401d8a612e7740ff9aec5d1bf78e1cdd485e01b5f45da6e409b1372fa0b52e3918c6952c0f4058357aeee776b1eb0fe31615ae1a995dac73fc84068233b5b3d4d2c0443995b3442b1589f1f57f26667135442e41c7606997f4ad481a018401cd99a376e677488e1f5076c82ab2e194f0b7b369cac2261bfbc2dc749e5644e4cac3aec5db648707ebae08d1b43e1e08266b4b6c8d183b337f920868783cc194efa6d55bad9ccab2309e55426e95cae55c5bf20260389f052b6fc7d796d43a02bfc8f294bcdf1edecccd71af0bdf568bc6a2a143c0323f6e16d24e4e4ef647483a73c2358376daaa34b9478f095940a9201bf2f43ef7c6fa25e19c45eb874b54fa52ccbd478ded0e3de953f0a84237e3ecfbf4a9a21d6b8f6a97ebd3856a0b4b89ed4947e8311a33bdc959fe95d9724af0acf8a89cc6f5ffba212b9211363c6f004e7925526f1ea38e89ec23b3aae4a1efb05b11b2d8a5f4b337241533e38baa6907e14d6c31f1b11350435d4c5a9b8e851386cfcba6c3ce500ad123830108c4e2a5edda8627240b9fd9964939f984332217bcc00b579e87c912114ecaa2180b6d3c3a436e0ffaf9955d1707942930299d124a429d26c07bd349a0bb132bfdff861bd5bc5c15ce84abea3d040f77404c9ed1630f27cc11d1f885e56bd94703869989bfb52c008c287fc503b42fbc6a8f5f74b46f5844fd2dbca13d690f9bc2b620964a4dcea4bd1e8019683b07f7270b3610e57837d57fb6a84e06ffedf589744b84e19fe6aae8693eadcde28cd2be2990ff247ef0ef3f7fa70f83f3d67098b8502df4625601fe685b858710da059cdfa4e84bd125529f866a349a22deda060d4a1eb59428ff5aa0bee006eb191650cbbebfe34b618bef661fae3411220328547c1fc2a1a6067e270d84d6246df36b516bf49979c37f8e7eaf8f38bc173685c76db015af86b5cd34825dcf70d3b54efa55e9163f25c1de29c1ff2a2a84ed008722c9133f3f989f37e8f2227a43c5206d98fa1ad33894acc45e4ad6aa71c74df1f00efd3bd69f11b67626a15d60e1b86ee5716469e2d4af236d50e8f8a3dc38d754964a828ea42d691283cafd925fe7cad83ded6f8e79ffea0704e84850d6f1d695e1cdb28927721e0bde319feb8bf68acbd041c2464b30674c0f473ab857844a1f28292b34d9212765ae4077bd3f380c52e66552fe60a393607ea2f8c4cff65255e3a856f15b14def092b67083b0a9ab5ba31c9490e19266cd029c4b91f8c49365ae03a9494463a23a9effbedaf2c11592724098e357e50a2cbe3a1523fae9a7a045c775d29ed6066a7f4e65d30ad9f8b626ad3b205b6bf60e22a8cb59d250580441628ca648ddc0dee4164975b4cb0e5cfe686086197aabc88d6daa70fe992d48b86b224a8b123daec21352c2bf4b08664e5b90fe20dc7b378767306e9e86f570c82d5f05c2bb266ad577c451e95d68acd7ac57ae60744ad9f4161996fcad0d9fc471ba89a8c8f40b6d688286859f8c70bb9d2064391006767fb48b4b601450d2e58885399a6b24ebd12cf7e759dcb8fd30978d2a1ea467a5d24b1ea2863c63e5d8c16d608eb0a91605a89801f37844c782cbb91dbeaaedab476f8c41a5bd06f6c4e5515c2d9767a560b07e6c681c4308fac2222892bb349df2cf7331de0894d7ae6134da12da5807aeb7b685ec821d16e82e5c9b05e2cd79cf6c815fa66cac917b16ffadb726965921212b47be2619a7800fd2431ad104bab4b0ffef4b657c03e0cb9a8a66f88ed8a62eec2654bae77c21b3f301b06dccfc7a87dddd94bc53c65df57b2ef5c6fb2050e685546cc0097db0e5d75dc09901adf32bd868b9176b60ca4aeaa3283decacc3c174029a2e51a94311087ca4b9d7d611df8f0f79c8cf917cee6573e7ca3d9ab7189ed95808e934e94b7047a22c8d070ced1a2748176b4d1eba8500a73ef3d858964143536734d45a9895a15977d2ec80df408da20b2d516620dbda0afbc60d16df6c446bc84dad548c698caf8671c30e1480ad5ac40c1bcfe9a16b7240b59dc34b5a56b4a619f348727b1f215ee0943b90257cf776b1ed1ede32cb6bd379ffc698220ec2e1b293992cf790defb28b56fd37394bc88cf04efcf92033f0ce3f34a6283ef7dc837ad9065ac6f70a941c99a3dabd217bbab8da3c269228c93c99b6333521055908e91d311b84cbe4bcdc333dabd3592ec8b988a34b6bd7cc92dae7915096e9644176c84c5e6709edfec3404337e37fe11ce676bd2aa1a4e9a58d025142875bd52f8359434f6cddf05450ce870997f038a80b3eb1c0d8f0e469e2d08e9ae6f9cf0bb61cfdf18f332bc035ce8452435649f54fc83bd912be0651670dc0ce4baafd1ad4fcc9f5290b8f2ba0ff44696c9132dd52eac26bf147206c09b530a96404f122be01bdc9635df50fccd36c6f94dfec1e4e627e7639f281a22a59099e25a2fe6e3371747d733f9865a6509078ab2528bf74b19665c86eae5fb9b025279bf1059cf4d6ee8f4e12d08b0c2f46caf6e682f8ef6717ffc965959cc9eed146fa5fc6882d10f271c96fd8c399debe6eb5d4ca153913c22e6d54cd9738d9dcbf1c8b91a3c2c2d912576e4e6e35cdf35c092202f772e7c33895f64fa899d2280f5206759029df5cbaefc2b61e40bd148c611a6a21728345b40bf6a2475bced235e48a34907fa47edbca39e8f9a0e0ece57b6bea14efe79649f75379e554c41d8c7017ae0c43495e5c5adbbe14875f3527638c7acc56266907f26aa8114d0f2c65188c2e913c003669b26ab3f22d4082198f9484772a4f3eb8f9d9d2936926642a413577c8a472c611a60d55bc30d2a7b74eb57f95ed83658528d15483386844674b42f8f20621c4c9b14d0c7c5aa6cda1c90fa904d6c5164320f0eac1b60592f1deebf47d5ac143865323931cc470fea2e060379629bc341c2d74abda1ec4e1bc8891c75f0e94cbb9f7588884f88b156f61dcb0419108923f63a85ce06db231c77dfd033b974513938644b7fd19f2bed75a4da4a5f3ce71830c1b806e814efea3ce26291740c46ce267e4d8a7e90ef6e34adb0a45e84abc76eedd3a4c40720c1a07c940472d386e637f6003e2252d1310a71f0ec0d3d43016ba2e1a102abcac4238aea9ec272c8d8b91aacd87802874795164c0d14bc5bb0bf1c67179620d45f11151c74a6b3c872a5f2690f6344c0214f2e0560ffc7aa7f7a2223b53210249af1ebed29afe7610b9b80c5a38b8a11b8e017bce930d00a3874fe364b188159ff9c72177f61844807c331da92fa7eb0b6308c1705b60106235c27c49385e6e750ae9323c39ef2ef9f0eef7a653e8115367d9769518837538a8b809ef42b04b9dceba2a88d5a20e2a2b23b8288057e9e7034ee34f1e8bc8f90bf3a08d58f1a2982d031da996d0e350f28fe3c3dab8ac5f37131e95b1052632628df08d6f0686fd0c0a1a32b6277a7cbee4997823d6327cf6e6a72dd0abdbfa908038c086df5a3c34f9909f4b0740bbfbe446cb84df3b5bd0ba2ec5901a74f20f22893167be220a7a304c1cff622d8b606bd3068f7c01adc39fd127d97acd6474bfbd4f0dc87e71a3ecedf7290041f0c591547a63ba0b4267c1e6ef7c679d077dfaa4977d70e2397a773b72bc0cbafd92389e8cc9a8ecb70e531236929ed8b8d8909accb8aa00a3de64d8819a086b8d0fe0ef6ad491315cde572ccdf7119a1353a6dc2aa94cdc4bad2f32892f33505cffef9597b2ca3720c983cc4ce21df1c9250d3bb3a4cab2131a836be7ae46f89ca2749be1a23cabac7fae8c70655d955f9d2bfc62156ecce51e3a54cbb5667387cbf8ba3b4b15e4f8032944ba0638df2e0d68f6e1cbfa180e398dc90c9eee07a70fec91a7db7d5e4bd92ef9dbb1af1481df13c6fc89483f5abc76564e658b33245e7035b464a1ee48c5a38fbbd6c5017c91de16088a74018a8ce2e5521f262117179c9e71300f789164581f2bf33f9a618d3aa2acdf4f596ffa8016c586809a332c88e9824267fc2ba18a8c7901d86cc53f56699643b225b0574a6addb63cc6c9d6607fc4dfe4af97630873b6bde63c2ac39c0f2a82a1040ed69cc759414bfa67791ba6b477b2e9e9114ac23c5aa921f27d327089e183477dfd0fe89c61204b423e95db8011c43b4e517dc84d11691cb8157c8b9a62fa317610b9558977657bfcb0b7b7a1d2aa56f10c13f95226d73d15cb677a94337b7ecacbe108d058b1ea79be9682f1556475626b6f9269bf3c39fe16665cb4c3ba0f26eae37634f9b19e91854687e9262ee86f4a0662716f4d4ce07fd57c0bd79313eb8eeb1a8db89a545b29f862abdff05175df4601c0ce84f4e33edeceb8e15d919227ccd57421bd41c6ee5a480cca27b34350654192341161fc89b54fa2c79f0c9c1c6a998cddaa76030984a3854f94e93c92bf2ecb1278aa5a19cd353fa4edd4f100fab04e6054687ea1623859b5238f16b0456f03b06d911b120b8499fb65dc57ca7e253f41ee9147f96c35d318dcc50cc766b6d6e2a48e0849060378d74ba6d639a8597a8985ad3d75ac43a1cb33b5a2a89afb4b2dbe1c96e89935b58aed7c03037349af452fcf230dc2c385c48cf667d6681141dbe584a7092745699cfa499de75538dd0f742e97b9ec0e1fe7f6de187216707865239cd7f9b175fdeecf5a49d97e3e9fa8209a3bba26e59fd8605402ab6b21cc5aa68b2ad69be80419c53e81542c12a3458b6f90c60c2e25d0ec44b87ecf7e10da4bbbe6a160d84a155007e89fa119fd813495a87c058ed945434aa2ace46bf0d23965c0cc8963b3cf41504023735348fc232495d24ea23c539b635f536ed2973ef9854d1988cf9777f426a53c59a9c259b5af30853d0b4b55151e0c48e81122c7c5178320fb73c1d4575520656eb7bbf5ca80b27fed31dd526e61ec0e40810358b70b295aed8d08a03bccdd84ff5c3409a1f23a9278600fee178a97acf5c0ecb81ff68d270c73d111eeac68b6f531005d63daae7324aec31630051428266a40ee56e3ac6115477f99c3d8c4975331a95d7f7762ef9a8b809c181dbf1d2e8c81be8066fd259e61588061002f4844000c8867ea4761366a790e7ecfab71d57c06b629693f13aad20110b636032ccb575134feda04401f09b05f686aa3bfad9949cb775ed4c2e320afb26f4fc4a2564c593a52c3db150df73939ff164bfed288fd442cece64ad461d71040abf778a0345d22c9745b4b266e9fae6930c92217bd41d966d1d3911945317a53b9216190fd188adacbe84d57a3a600ac1cb50e361b27ecd9d116d264333cd4891a00c5990b3782c86ac745b6292f6623dbd2fb730bb7aa518564125c56ee126e83f3538c98c453b8a87c79cf4285f8377ea8469806c3f99fde2d4225510724d9fdbc6d9d41b01ff70804e5b8e6286d5a0135e256d8725eb32b95c1582f0f296fe3296453e4407d72d474d459bfa4d3a2852e1289f8a7d1a04ef42c14704b577be4448454dfb08ec645cef675f81b1d3547497615508121fea69085017f43874ece7c54b6e5bfc6307a5231cafe2f5191ae5c136876427542f73439cf2aa69f521c69cab2ec89bcd6a290c594de1d951396d36f9162daabd3c789ff63c5f3f5be741dffa162efaa8f2702131d473f1b724b858f8aac130f0c8139c7f553c04c384ba67508e444d60a95969a13b952b6a7e890f9becb83a09f097c40baf5954352c1987b5b9db451e526fa4d83f122f996383b3b582977faf402c393cbbfa45581eea8957ea3da64d49d94157ffba4169d2362aa9b472180d737bc0a55078428eab0f9eff43afa68d92bce3afd5dd0ec87ffa63d7d079213f8ade4536fa7f926bf59dfc4a1a1bdba630ce72e103e18292b5dfef65d6bc7d8e118296ee2c0a6db412662bbf353948b9ba88cc8339ba90370ba7316b33069ed08d822926cb53da23262087a7ed64f6bde2b73c2dc3c538a94c3b9a3a163ac85a181c7e8866fc19052474076a8a6a6af50fed2065554619f2fc0a38653d941d9fb4df3b74ab6e877baddd9f8cb915cf3b2b899960190c7f8caf3ec6c7ccb3e7cbe2bc6547c4908a49db4414c3e07c086309a2e4676ba79d310f283a38c1f14534ff39e6c744ea72ddec43b0107a8604b5f34bc9a31f200befd27a9bee1091e2deb2f8f089aa1bc76f4b09fd21886062959db554d28f328cc5e1d2ce22f055b1d105a5233394247c0ce50b78a1b0298e5114bbdca643c717a8271de9b822971d1810d97660f1d6150a9d0b95d1c227e763c3e302c1fefd3f656f96aa4c63ad340b5b4380513c30531f6113adc5b5b48483f9239a8f8686d875bb06d190237fa3d92928c941fc1384317b35e902f5b04d0cc5f58c8d4d9a96dc68e4240487b1fd01208b35d6263cc7012175c1d1ce708d8969773ac35a175f1679f03b248a38ed8519d1b48d8ea55ca82dec86b0c32235c689748e29adf0ffe8e04dc823a67a1bd220b98493e91bd614661b3673f146c68b3062c78e9c839701319f179f36d682610ef7fd929095b2d763231843af417e228e551357da3e8ffc465b474bc930fbe6a66ad442e3b8e595e7ac4c16330e00dd0ebac867feeb40c8080e2a02a42517fde15fae36c6fee9e3afdd4ff3dd9b8e0c8927b0ccf47fd2cd4a421d239143e0f0b0058156b69c1e64e776b86d4cbbba54fef97baab2853f7965cb2fbab3201a8bc5d3df40a66bd2a5961afe862e40acfaf45c56ed39250b49cc152c33e90bad4bb9d3caddd0bc9cb0d39cabea9627691055c1f226a905922ef23ee145029258a2c18d54b77b91efe9e560b38d3544812b8a40909dba0d18ce662dcf3db27e9fb38b056976efad122110b45ab87c0ce9130581cc2026fb8713d08ba9605a97c2a6956d90668d6a48ea0e052177eab58464407ec2cc3d8049928b1b605fe71b55f693f9cef163e5de1f576dc8f3ba5b7f9948602deddf8bb7d7e58873f25e9c119275366f07cd50faecacccb346aa5ebeb05d79a91f796a2e1b93539fa7a45aea2435fc30124112ca6096c24f6e807ccd68fc1b19b9671832b15ca8f557d22f141172529ea5a0eb0fcd96028ea3f39d464560a891fc0303c2f0ffb17c384e640c5ac0286ad10ed83f8ec310a886e501d8a88f12b2aaa3e12063a9801ca75278756f316a1cbebd51cc8179c24fa4f7005fb299c9b5276c693d04dd710ea50361004520f182cbb678b9816b82af7e956b64f5556b72c38b3cb1ce5a83e5952b18f42e34e561a57ff95d7ea53dca061a724bf8a4a72a253695d1aa90682b41f51827716a3354c1db501d49f53c101b1cd036f8a5ed1148c8f7a92fe63600ab357796ff10287f49d6391111ee24c8aea92b1d6934efe840cf65258652ee471ad64a648c286775cb5d1bccea27dcd5c01c31eae56ccc18b197aa409818c49b961c8920094528f49786f33184c52888bc3cf7a448e0e9445919343f3302244d6e671136dd32b0cad87d2e445e479cbd18b7d3b6997a2a2cd9c8bbd632692367847795bc8532e5bbb677bf89c060362ec61582d121f4dadfe9c8da63f89a99493f380a611718835b6dc9e37ae9c713f292ed074bfc770d79e467c510b112b57fa0defcd44c5bab5281ddd415e1d7d1cf101e5d9eaa1be9d0727c764b061f67a304a1279aac6a8dc66bc65149076b73753677ced828d0075e1739eaff6edb59b99e8bc3c62a200566d08da84065127a5eb1ce6e9b9320b2d3ef42d33c07fa4d765c29871fe552466bff07f7f204232aafb81c9d4780938b0d969bf8720054abcb8d445d44ec8c2243cc8de44b82024374aa6852994678f2d38cfff9cac17561e6fb44540a4c6ab2fb0d4b2046134b2b4ccf4ba8c6006d4245857fdc8a72209e5fd6c667514bc03350aab2f1faa19fb1d8a340f5704a38d2cf800f994fbaaf0d50ed24d4666e72757d9705b60748c07e07f1093357255cc188463e4fb4c34cf285ef62e969753226e95250828c21b8dc015cb11ecd026e5e32375e03128df4ff32789003e16f88513a3ef9d6174d93e2a011af1a0809a3c0d13a3f88c107fa8601ed05134a4eb1c3f634b6d9037f6c000b2c480dc68667c6446d1e050f9e7a43f9df958c28471ed0e335ec681b3e8824b8649f32575d46f1eb98d86a23200ed92454ec1b079947439b1bc2d20104e3684aaefe2251891b2da54f3354dd08fbc2ba537f35c85e2e5217a6be19ba8aa17cdd2ae81cf202086ca2f06767eaf5cf85fc5060b528b5b6ce6e33f213bd82319376cf28e055ad28900922396b465beaed7203ccf4699e937a3c997639c25a9cfef601bdb6b34c5e56cd0f586a20fc2e75bbc49492220bd7ca9e5e95d19afbe7a923e02e81f024c2aec62f7ad986d67f637dee4108658c663abbe718912b2629290595716a291b6ba209f655a0d465e7307dd36fbec72abd38ccf9fd43b85a3c3a80635da520ab8810251a840143d9a31564912408efe5faa2db3df744d00cb9cd50efcabb609e068306ea9f4b68f1c7ba59d6c4cb65f90a90850dc3dfa09150f1f24f26878292c6015bae8a24fbdf2e4a012736f7d02a0fe11bac9826fb26e012485b476cb006de9680176d16f3ea9c5e73ebe8607f0f77bc40688774698f9be590979678808bdabc8549be9b4beb31a25149080e1b38d7a4dc329a2a0aaf72074d8a1170ac9fc72860bea7ccb2c7aa10aac3e4f847218564b33764db0b796104e04ebeca4724496321996167f8ead4269f9b6b81209c6cf3687f4bc733978496fa5453bede0e5bb914e299d1759929a64bdf07545920ecfcb44747f3e49fa50901a73dd9a291bbed0344535d28b009d864411f471366eb511edf587fefd70887c9f77f6b0d031260907c33941fdfe4b1eb51d28c0ae0dd607b09ae45d66979bfe470524e11a80c89209450a36f44c0e6dc2db7a8979258c204d70c1b691a3fae03bc6db9f11cbb7e39756d112443848d51b69c14313cfe276d3" hexSignature3 = "0e49fe1706d9e93b3793e1057f328c4206632da58c13b1aa3c95158c1a68b8b5d9b4fb001100e13cdac760febf755faeefc44667db4983225c5db714516b867a557e6eb0210d40649a0242f8602e2def3ce8a9fa2d82439554eb21c5bbd96d131e28d88aa59d7761c0f92cad83ef4fc21ee97c09a81e5ae20a4475b68935c3ec4d06c1d2a2165505b667b2741a5311d2a78d99cb46163b8b6d65d04ee40c2d3e5af2b3bbb2aa44181c012f253b1ef7700114b4e505e4c0a672c7ae8b893eb056e7232b5e9cd52d09b87a8025e9841d2fc2b890fe0a0f3c4c2c8ee7b464f4e1fad6c891d95339ed8598f7c4be13dbd5ac404b0a655756ac020aa827612b4145cd96f2b242832c9a0aa14aa957c192f52642e947390c188e0e441c43f458dd1aa29dbbf5c9c883587a3ad43770222cbee2dc5ee22f0050d9490a4b1a278206cac70754d4196ea1982eb72df2d285f27d774947a05708003f449cdf19d8aaf8e63d4339b9eca58b2cf9168fbbc27ea6f9b65af1651193370f70e3af4d1cafa7a2db7fd122cc34fc07fea2ecdeb168949e83a3389bb58bce50aff380c707a2b6f66492258e6a8108630e683a51d9bbd15b78c313e5f3b808ffd904ae27a55fc8ae8a21c4e1518267745f3358430e3df11391875e30fbf191648db283dbd98ec0451f07410cd571fe51bf34ad00d04389013c7652af126d5fdb61f67b1d5ff2bb4e31c714b992cb9a865f7cce66c0d420fbc3d586cc45ae651d3098b96481e69af0fac22cad38db480a711acac3196440d2811ccfbda72551bb9fac7aaa3fb8ceb35690922c9b5b3717c3ec3a97a0aa8d0b42f5a4eaba8f7f2e6312e4f4dd239f76870b177feafdd612a25edf25943ac9f3c7dab34947d728029ff5d2bb0d01e79784402216ce5ff3550db5eb38b5e0f7964afcf2c0a9f4b7e4693f90df9c58bcf2170a240489f62ef7ef8196bbfd162e13d8fec57d81ed815569640c9b2f09f9435fc2ddef0cee72cee5513e8e4d16b245ff83ce64e1c2f8c098164c193f951591d5139812bf64e075b39674d09bc544f1481769bd2088bc04ee2e7e8a7ab910c83c5d87cf998c66ae4b85cb147bfbbd46dfc33f762a5e8575853cb7b96291d54d4fee7bba83256096d63dacda76d4757a1655098b96b93da7290c1c56607e9028a6fbd60a04d800a39c35ef641fc20b91e2ef296b67ba58c3c9418544965df7fff2ecb73de99c214dbd4574e84414c6ef628b087c9811bb93e97c987d805df599616edc2a60cab5e866c7686c57e1bac8632cac62832894efe0c075e2ae689a86b8346bd6f553a11824edccefc08b03cab4beac4cc10d77555ce484655a7ffea197cdc93c34c5d7df96d4bd92c0a3516cc277ffa1a14d23ad64fe7609dcb9f34bde74c128268218fdb52258f257c4caf22f2337c6c62ec1f67c423e19a61687325894c01c5b436d8b2ec934f800bbd13b87c5a5c7bbe0a471496953de8c6fc5acaa388718a745fe87bb127c0a4b390bb8c865513d2cfc0063c38a41f4aa01591901009a6c0b0354c42ed55a6da166d7c109c676779e83a2dfab81392cd55cf1262b55aafa031b7cdea8b2c1506143ca8eb261f52fd87a36b00afd7c7e69e28e33ed5d978a3e1bc2743fcf09dd6c9cf6931e8a53c18f184e12f3b8c10f117f43664edbcf5aba63ab383397ff2348450530f986bafab58e2f5d99a80bb854c6eef15364a37cf39cf08e86bd71accce49b233789f54b4b891c2214da4d1778d4d26955330c28ae9a061f156b048dcc34683becc0cd9ce5ffbd2968bb4f1445f742212a0b49536cb2e8d03dd8af882ec87ad0d08f9681ced02a23889aa2396d133b2a13042cd0f915357e4c8c1b86fe92398382634d54d2dc93c42891dc8b193f9790fe54ee58537157d29af2bf1c1c6b667c365b1768079053dd1a9a4617a511ab7f4008548022542ffdf697e78d93240bd63d13977cdf39ef936bf0f8a464dc7f9fb289b694ad4543f90b1338dbed53341053916cbb8f645a29524dcb865022db71e6565839f8ecee01ade85508634a27e58bca4c4d224b26a4c9b897514254ab0f1eb5f4b487a5a2e35cc0a7bb111a5c9313e99eec8ae21cadf27f40cdc7cad2c2429d9ede0d8328769ed2b6fb3c00db50c2539a034b7c960da37eb6717fb3c71cc47aa25fdd9b27c8ab24455c9dfa934267d101de0197480a2820b5c59597930675a1d3269f7c3e033cb41dc08001dec66224a3d2364f6f14b3991ca4726907a5a10e5216d735603faf966e7d44ea86d49fca2e31e613ce0eab7ba2e90c3f33978b15c9379f9f481e4a427d5b9bdc3f2e0ab5288a1f0b46e8e4c4af023f65c4d7ca13f0ec4bdd50821f90ab8806f5d33689de6aad54c4d9a8236023635164b64001c63a3569316ac5c16ecf2a7678e9bfe1dd3f1e3c213955895ecd8d4897b200f86d8ff3789b242fc5b699879a9033ffab65ffc6e1bcbf96ee9ef41085b11fc362ce9df7c76ba2d66a0fcf10dfd14bbfe75498e8e0e87ff12ed1d4e9a250fca71e187109b7aa35b30eb38fd6252b35d2f5c3cdaf7e1f2311ea6ce6a6de6d1b681ebd6af74469fa25656dc38b1dc7408c81acef402c74fc23be353d4fe5b606bdfbe22e621f76c581a9f85b0d7ae39e58c078534ec76682067e0ab85f2aa6c9d46452c0f4058357aeee776b1eb0fe31615ae1a995dac73fc84068233b5b3d4d2c0443995b3442b1589f1f57f26667135442e41c7606997f4ad481a018401cd99a37cc8e878ad05df4b22f92cd5858864fbb7edb8442d1045b53374f24a6a233a1b95db648707ebae08d1b43e1e08266b4b6c8d183b337f920868783cc194efa6d55b2edd1ab0ac51e5f46eb1cbcec86e0a3acab54fda2eaf244590390c4a163672778d39db4b4baa340d9eb4eb0b7aff432ca76552b5092c0cfc1ea55c702b2c5ac58376daaa34b9478f095940a9201bf2f43ef7c6fa25e19c45eb874b54fa52ccbd478ded0e3de953f0a84237e3ecfbf4a9a21d6b8f6a97ebd3856a0b4b89ed4947e8311a33bdc959fe95d9724af0acf8a89cc6f5ffba212b9211363c6f004e7925526f1ea38e89ec23b3aae4a1efb05b11b2d8a5f4b337241533e38baa6907e14d6c31f1b11350435d4c5a9b8e851386cfcba6c3ce500ad123830108c4e2a5edda8627240b9fd9964939f984332217bcc00b579e87c912114ecaa2180b6d3c3a436e0ffaf9955d1707942930299d124a429d26c07bd349a0bb132bfdff861bd5b692badf468e8f4d346c015bcf2f3c26cb6ef7729a66cce2358c024581877294bd5247b0cb67243061a827a575b6958ba2ed809c55d75f898eb3074de5e490f43452e4e8efb7aa763c26a45bda0d99a1e4b0be489f528f6d6bf83e6dffc2094490a8e6407440d53c9ef7a50d179e2e273d35e37529b8ee1b3f0227f91cb1889cd25601fe685b858710da059cdfa4e84bd125529f866a349a22deda060d4a1eb59428ff5aa0bee006eb191650cbbebfe34b618bef661fae3411220328547c1fc2a1a6067e270d84d6246df36b516bf49979c37f8e7eaf8f38bc173685c76db015aadcf93b7e7ef105be8c4e89df168487d9dab0bd9dc6b972f4ec071242f901841f3f989f37e8f2227a43c5206d98fa1ad33894acc45e4ad6aa71c74df1f00efd3675df3f08a00e62f03010fc9d049ccf8b0a676c6f1603959e1202a6319706fcd28ea42d691283cafd925fe7cad83ded6f8e79ffea0704e84850d6f1d695e1cdbd335e11ac75abfc6ce977b28688242fef27de931c52732b6eb1d39020f49f5c5cf48878896de5d412435c79a810a5ccff084c97e441ffa159170347b72d67abb1b51d636b6e1d2e2dff08c96b2201afa84fc43143ad05d61f80302ce457aacee9494463a23a9effbedaf2c11592724098e357e50a2cbe3a1523fae9a7a045c775d29ed6066a7f4e65d30ad9f8b626ad3b205b6bf60e22a8cb59d2505804416281fe78c2abb72ac09b4d5d2237159f733b53a699041ff1800488ff3ac90739d2b24a8b123daec21352c2bf4b08664e5b90fe20dc7b378767306e9e86f570c82d53d025babbc546fc7094c067da9836e189140bd43734ef9834be3e25e235dd9a3a306045ac98a244c25c472dbc226505c8cd8da89f68641ec3932a18b720bbda05399a6b24ebd12cf7e759dcb8fd30978d2a1ea467a5d24b1ea2863c63e5d8c16d608eb0a91605a89801f37844c782cbb91dbeaaedab476f8c41a5bd06f6c4e5515c2d9767a560b07e6c681c4308fac2222892bb349df2cf7331de0894d7ae6134da12da5807aeb7b685ec821d16e82e5c9b05e2cd79cf6c815fa66cac917b16ffadb726965921212b47be2619a7800fd2431ad104bab4b0ffef4b657c03e0cb96a3931440ab20c977e8be1186b090dfcd054abb01018aef8047dd1d5b1fb0462b2ef5c6fb2050e685546cc0097db0e5d75dc09901adf32bd868b9176b60ca4ae3754c136e4491c4043babe09c5d0a54cd5ca94553103ebdce73650677096271f573e7ca3d9ab7189ed95808e934e94b7047a22c8d070ced1a2748176b4d1eba8486eef5b2ffcdf76c944cee8a9afdd7dc7c97aa3782cf8e87019c27b6d69e7cabda0afbc60d16df6c446bc84dad548c698caf8671c30e1480ad5ac40c1bcfe9af473503ae8b87cd84165716203f6d5432b972fab50932da8932bea64f00065b12cb6bd379ffc698220ec2e1b293992cf790defb28b56fd37394bc88cf04efcf9b2a6cf53db75817127c893184b7e2f2e12bad7b726ce8f06493e3c6324602c149228c93c99b6333521055908e91d311b84cbe4bcdc333dabd3592ec8b988a34bab8b8de5903914d0da7bcab87352ce9f88ad844097bd0a5ae40fb04c3194b72fa1a4e9a58d025142875bd52f8359434f6cddf05450ce870997f038a80b3eb1c05edfbdb0fdac634ec05c09e1bea594fcfab009edeafa0593ee7c335eb381cc4912be0651670dc0ce4baafd1ad4fcc9f5290b8f2ba0ff44696c9132dd52eac26bf147206c09b530a96404f122be01bdc9635df50fccd36c6f94dfec1e4e627e76dd0980572a80ef7e49f89c0d50ae10ac438a49efd1a895bf0c9a23d67272a3c2c86eae5fb9b025279bf1059cf4d6ee8f4e12d08b0c2f46caf6e682f8ef6717fff72f6ab8ad6d9a9c936615cf37c349e627dc3e91bf354a08b84448234fa2ec9cf6c545bbba9eb39d08688c53a3f2b60f9e5529f54526fb993fe2bb5a5fd367a80575e5100e36661a9874f3266d1ba90d46e3e7e2dc925dbacecb7491d96ed3fa996aaab025ad207e56fc8833bc58e25f395359af5f3493c1e127b8c75be5a208d6a538fd5114163b5db44ff90e7fbd493ee902b6c275a9cddc129bf8e99b74c7f26aa8114d0f2c65188c2e913c003669b26ab3f22d4082198f9484772a4f3eb8b0f65d734f6b8d6851bd1d26224c183d38acc7347f7277136aecd8efd7cc259028d15483386844674b42f8f20621c4c9b14d0c7c5aa6cda1c90fa904d6c51643c69f158576996f3b42bb079d1489b6723fb23ef7726a1a0c19c9fffbc9fcd8a52d74abda1ec4e1bc8891c75f0e94cbb9f7588884f88b156f61dcb0419108923ff4c90f28940dcd52b0e0eb5fd255b7b698358e1c13cf05cc6c2e7b89e25968d74624b2b307494598b39dcac86b7642f02bf25559915cd42ed57180bee8cc4323c365a7dd2fa9682e44b7f733d2a509568c43a0c484d28e5825291faab9ec21b6ba2e1a102abcac4238aea9ec272c8d8b91aacd87802874795164c0d14bc5bb0bf1c67179620d45f11151c74a6b3c872a5f2690f6344c0214f2e0560ffc7aa7f7a2223b53210249af1ebed29afe7610b9b80c5a38b8a11b8e017bce930d00a3874fe364b188159ff9c72177f61844807c331da92fa7eb0b6308c1705b60106235c682f824f0b37b8e0ccf895e7365ea3830877d879354785c62329c4fec12b825b809ef42b04b9dceba2a88d5a20e2a2b23b8288057e9e7034ee34f1e8bc8f90b3edd5c0e73f9140d1802ac19cb0ec060d2c0670e116886b4bfd0bfbd8ec2b525abd01e4446093f9c2064581b0736cf28c93d5cd11d1f80ab7d64faed8a99f5ad898a02b3c460dcf095adcae69b1791f319dec73568ea112497c7f861c437464a22893167be220a7a304c1cff622d8b606bd3068f7c01adc39fd127d97acd6474bfbd4f0dc87e71a3ecedf7290041f0c591547a63ba0b4267c1e6ef7c679d077dac782f235d0460ba8da668837008eb7006865a4c50a09130e27d26aab97c9c49909accb8aa00a3de64d8819a086b8d0fe0ef6ad491315cde572ccdf7119a13539eb6c7563356a29ef002cd70802e9110f770294c8255ef9ed86e612897a7de2f0d3bb3a4cab2131a836be7ae46f89ca2749be1a23cabac7fae8c70655d955f9d10ad64e0b91b5a387a40c660d6b5dd7d1a7d5939f72cd0a4e9f82d1277709569da6713824282fcaf4684c49b2081fdc82a707a93bd62a1c679c22199e5c1253d13c6fc89483f5abc76564e658b33245e7035b464a1ee48c5a38fbbd6c5017c91de16088a74018a8ce2e5521f262117179c9e71300f789164581f2bf33f9a618d9245b5afbfc2e0be97a2fb86e2f228289d191544c286f2ccaeffe54263c41af856699643b225b0574a6addb63cc6c9d6607fc4dfe4af97630873b6bde63c2ac39c0f2a82a1040ed69cc759414bfa67791ba6b477b2e9e9114ac23c5aa921f27d594629d55f2cb9ef4160fadeec9a3e8dc1a2c6b1569fcac431e447e1c89899c87c8b9a62fa317610b9558977657bfcb0b7b7a1d2aa56f10c13f95226d73d15cb178a8745d42f9dbdd5ab8af3c711350f70bbc33067472e24220071d05aea31db445a57eab0be0f7527180cc2b8283f978fe2da8e7f07b80b1c422225f6d66d8494ae8b3c72f8f785f306376143f2c483392f14b637706c7dd42afd0d3567c74d85afea12c95de543eece7368d445f0f48165c4420e760f8cd1e84e25710c7717b54fa2c79f0c9c1c6a998cddaa76030984a3854f94e93c92bf2ecb1278aa5a19b6c26ffb2efb9e31ac9ab7d85316a2d01c1737eae5b759ac378e7a49ddd97a440b8499fb65dc57ca7e253f41ee9147f96c35d318dcc50cc766b6d6e2a48e0849060378d74ba6d639a8597a8985ad3d75ac43a1cb33b5a2a89afb4b2dbe1c96e89935b58aed7c03037349af452fcf230dc2c385c48cf667d6681141dbe584a7092745699cfa499de75538dd0f742e97b9ec0e1fe7f6de187216707865239cd7f9b175fdeecf5a49d97e3e9fa8209a3bba26e59fd8605402ab6b21cc5aa68b2ad6cf1ec42a225a94f892f927a47da041cbe1f09fefde216f72f34aa3d3530bb44b60d84a155007e89fa119fd813495a87c058ed945434aa2ace46bf0d23965c0cc8963b3cf41504023735348fc232495d24ea23c539b635f536ed2973ef9854d1988cf9777f426a53c59a9c259b5af30853d0b4b55151e0c48e81122c7c5178320fb73c1d4575520656eb7bbf5ca80b27fed31dd526e61ec0e40810358b70b295aed8d08a03bccdd84ff5c3409a1f23a9278600fee178a97acf5c0ecb81ff68d27ab3ff0676dff99ab77313457e44792de597e7d6df62addfff4a341eabf9926eb15477f99c3d8c4975331a95d7f7762ef9a8b809c181dbf1d2e8c81be8066fd2568fe4409167da69df498ea17c9e35cdeaaad0033cb18ed62cc1dc5cbcd13d076a34d551096839b9d8e4db072c95a2149de3a18eea765155f69e927cc7ccebafa3e6065044cad620c50d3d5d6229a569ea1004391c9c62882c33e448077266fa9d461d71040abf778a0345d22c9745b4b266e9fae6930c92217bd41d966d1d3911945317a53b9216190fd188adacbe84d57a3a600ac1cb50e361b27ecd9d116d264333cd4891a00c5990b3782c86ac745b6292f6623dbd2fb730bb7aa51856412b347149e15f7c227d77721a736d92f52fbfa82fd83e7211ce2865845c2b2718cf40e337b5b9482202d67e90bdcd7255a4161f35c5592d2fa2f4dd6dfe0874b62ddfc0ccfc347b927cb85ccd274351bec258cd4f87d45c788c9c7fdf0d288d01bddc1118529dc5661589be8fef4a284c80e589d85f645984a3fee953c374de48ba4d948f2617b8f2f838ae46a3e9c4cd0bb740b5485bf90af1fea6948a229179177afc6e89932e6c1b41cc1b80ddd9203bef03bcc1a970eeb2db2b9dc9c7bb94daf8527c39dd66697a1592ab8af20dd9b0da37295615bc0ec7fec67825878abbe94b17e288adefe009cd89301a3a9c2668b26cceca6321afaafaeb543a2593f7ecc8813adcc055264024b327386e3ff6d942c2fc43325da26110af27cb8e701bbcbbfa45581eea8957ea3da64d49d94157ffba4169d2362aa9b472180d737bc0a55078428eab0f9eff43afa68d92bce3afd5dd0ec87ffa63d7d079213f8ade4536fa7f926bf59dfc4a1a1bdba630ce72e103e18292b5dfef65d6bc7d8e118296eee2b2481ad33c3e1d514ee029ba6ba9e5e8bdb746bd3bc376e201bdd6d1b3a9bc4daafdea39e90a7ff7841a4f85a4d0a5f68b1bba3a1db40dea7d29336718d6d99a05bdcedc42bc6f356441ece5ddd88a7acaf7f1df71a251405da9e7d259f814768bfe3d5a4be245ad7a8a002a62edb0d514e08aa3af1626e963e0217c1f26549f52f5e0f6ec3c62cd2b58cf021ac715f8f0de186fecaec97c3db9a15841bc1744ea72ddec43b0107a8604b5f34bc9a31f200befd27a9bee1091e2deb2f8f08188e097dfb69de474c4eb62878d0c7836582429fc4740185c09be235b7a1a3456d0e7b8e6e26c3d872d9ca713f51196e2e0cd1d1c33dd933115579c484097dd4351645aa1fe004a9c8b42a2a3fe4a4140c0af8686eaa68f5dbcb5a14a35cb912b11be046aa6820e924c4e37472d618210d3d562cfcfa59c4c529402d01cc79cc55d21b15c0dd801e07141e300b6e116d39a4ad00b698f39092e3520f813a68b56263cc7012175c1d1ce708d8969773ac35a175f1679f03b248a38ed8519d1b48d8ea55ca82dec86b0c32235c689748e29adf0ffe8e04dc823a67a1bd220b98493e91bd614661b3673f146c68b3062c78e9c839701319f179f36d682610ef7fd96df532b48dfd93ae2df01bb8e67360e1c3b08caa8674af3d01cc044a8da821ffb7ef0eadb15a7331d8ed6a9710a21bf5e60733af99401ffaae093bfbff02e45d6c6fee9e3afdd4ff3dd9b8e0c8927b0ccf47fd2cd4a421d239143e0f0b0058156b69c1e64e776b86d4cbbba54fef97baab2853f7965cb2fbab3201a8bc5d3df40a66bd2a5961afe862e40acfaf45c56ed39250b49cc152c33e90bad4bb9d3cad1d4c9e0d06b842635fa1ad7a27ef3f8e7451f1e36397f2bd7c9e5f46702bfd2777b91efe9e560b38d3544812b8a40909dba0d18ce662dcf3db27e9fb38b056976efad122110b45ab87c0ce9130581cc2026fb8713d08ba9605a97c2a6956d90668d6a48ea0e052177eab58464407ec2cc3d8049928b1b605fe71b55f693f9cef7658d8663519c05d3446280c059b9fe898ecbadbf763e346b469a22ad95fcbd66932a3368a1db50b5e73c9b66be6a3394ea2943e7b4811c5eeceb14ff5d5d9c324112ca6096c24f6e807ccd68fc1b19b9671832b15ca8f557d22f141172529ea5a0eb0fcd96028ea3f39d464560a891fc0303c2f0ffb17c384e640c5ac0286ad10ed83f8ec310a886e501d8a88f12b2aaa3e12063a9801ca75278756f316a1cbebd51cc8179c24fa4f7005fb299c9b5276c693d04dd710ea50361004520f182cbb678b9816b82af7e956b64f5556b72c38b3cb1ce5a83e5952b18f42e34e561a90daa9072a908cea034bbc8708c57d7cab14fb53ea24dfa16f895c3b34b468751db501d49f53c101b1cd036f8a5ed1148c8f7a92fe63600ab357796ff10287f40d03e0abd297fc419a8faea8a8eab64150c2ca54bcedae7c5c029da419398308b5d1bccea27dcd5c01c31eae56ccc18b197aa409818c49b961c8920094528f49786f33184c52888bc3cf7a448e0e9445919343f3302244d6e671136dd32b0cad074048e79c0fb0e364752d7ca59d98a8dde86df0bc388b6cfa855ac319f8f801aa72c003dce0485645353593d615b226e75acca063f311d7cd7e741614d69acfd3b07663a35a467a4b8c9c2032757dfcd169b0e1b59096286a426e6d1e35f4371ddd415e1d7d1cf101e5d9eaa1be9d0727c764b061f67a304a1279aac6a8dc66bc65149076b73753677ced828d0075e1739eaff6edb59b99e8bc3c62a200566d08da84065127a5eb1ce6e9b9320b2d3ef42d33c07fa4d765c29871fe552466bf55063b651b46821dbcff115928357d4481f04f0a82a918591c8b16dd91afbda8c8de44b82024374aa6852994678f2d38cfff9cac17561e6fb44540a4c6ab2fb0f05e605e4ccdfe04c9262f60e9fb0eeff75881f687e2974e7b978d61aa06b04ff68aff6b591eeaa1f2a8bc49969681d6e9d10d25933c507a9d93a535c3a5e306705b60748c07e07f1093357255cc188463e4fb4c34cf285ef62e969753226e9584dfd8da7d2b5808cca32a96ce455fbd1bedb2c5dbb6af25297d27b5e13b17c1ef9d6174d93e2a011af1a0809a3c0d13a3f88c107fa8601ed05134a4eb1c3f634b6d9037f6c000b2c480dc68667c6446d1e050f9e7a43f9df958c28471ed0e33709deeb264091bb83bc7f27c15b231cab7ee92968f269de7838eec01ac9e3e035f0e5ad991dbf614af42c543ac67b7abe46b7c4d9f4fbf3d2a45af33c7237dd32ddd69e522704b1c9dc726ac1e35ae57deb529a38fa572c7ace3f0cacbf3fc103f213bd82319376cf28e055ad28900922396b465beaed7203ccf4699e937a3c997639c25a9cfef601bdb6b34c5e56cd0f586a20fc2e75bbc49492220bd7ca9e519c6f1a6a404a3401fdb778dc336b8913c2d89cd1a5b806cf679b915608f4d4dec8de7c3457533f023222440cb7c7b1c3a3a22feb2c639b954238074dea7c9da9fd43b85a3c3a80635da520ab8810251a840143d9a31564912408efe5faa2db327b1abf2001d3c0d82df8c63afe6cae386820abac516a295f0fb0275cfbad22fdc3dfa09150f1f24f26878292c6015bae8a24fbdf2e4a012736f7d02a0fe11bac9826fb26e012485b476cb006de9680176d16f3ea9c5e73ebe8607f0f77bc406374948893913d128d2ab3831e65002019f67c49de1bda9d24f9236ca377c1133408cec5859756e9325283e8b312541a7e703c75b73e40c108fec13413a259f0e3a0f83bcd751bf006328c061ce8efda3bed6a5febd16be95cf955b1463177264aa14c2ef20e2a98b1784a3d82a2b65b5fb0960558b942c7abee33b4c1eae8ec37254224e6118c943e724db9b414312242752de2d9dcbd8475e2d8c3914964c73f587fefd70887c9f77f6b0d031260907c33941fdfe4b1eb51d28c0ae0dd607b0f1b5bb58e115d2c97f1508db724e4ebb629ebb73dd5e0913a76f2dc16274546dc3453ace2889935af52d947cf91b72e8d943909cf0b44369e68222586755533b" hexSignature4 = "0e49fe1706d9e93b3793e1057f328c4206632da58c13b1aa3c95158c1a68b8b5d9b4fb001100e13cdac760febf755faeefc44667db4983225c5db714516b867a557e6eb0210d40649a0242f8602e2def3ce8a9fa2d82439554eb21c5bbd96d131e28d88aa59d7761c0f92cad83ef4fc21ee97c09a81e5ae20a4475b68935c3ec4d06c1d2a2165505b667b2741a5311d2a78d99cb46163b8b6d65d04ee40c2d3e2289b0d981a5813fcb4b3f3fc6b11230fd4cffd3b910d7f766ddc09e95d3dd6fe7232b5e9cd52d09b87a8025e9841d2fc2b890fe0a0f3c4c2c8ee7b464f4e1fa0c0b8f4c57d0a3c94604ba22fa6cba188d776bf9e9e9e6471c61b8ccfba14fd996f2b242832c9a0aa14aa957c192f52642e947390c188e0e441c43f458dd1aa29dbbf5c9c883587a3ad43770222cbee2dc5ee22f0050d9490a4b1a278206cac7a2917be304c3c23a9e49574fa2d26786beb9948831cebcc198b2755bad06684c4339b9eca58b2cf9168fbbc27ea6f9b65af1651193370f70e3af4d1cafa7a2db7fd122cc34fc07fea2ecdeb168949e83a3389bb58bce50aff380c707a2b6f664e92476b46ba85148f4dddb5fa371eb28e50cf2800965c214d74298972cbee99621c4e1518267745f3358430e3df11391875e30fbf191648db283dbd98ec0451fab47d94079e2092a0a1879a6516dd0a50c1ec4f4e85545c59526e049a8d54311c714b992cb9a865f7cce66c0d420fbc3d586cc45ae651d3098b96481e69af0fac22cad38db480a711acac3196440d2811ccfbda72551bb9fac7aaa3fb8ceb3565882add53b72cf21b27c018938fe705b74a8ae2bcc2c80b0904dbe447968e66c0522c866c31109e6a055fc03a1696e350da0d21ba59fd7d530628fe6ff8ebb9e402216ce5ff3550db5eb38b5e0f7964afcf2c0a9f4b7e4693f90df9c58bcf21765491e9bf1019bfca3c4e0cd52b9762011ec92e941c4749d34e3fcd04719535d1b01380dd6e54408fef2a6644435af3abfd854f4cfc58031e90480ab3d0aa4064846af0e855bde2f7fc89a4a662ffc6affb8abc6d60ad7304952e30a63db3835cf4030adff9a0501d2704256c2c12001d2773fefa7b580472eea8b3cd1b289092645293e7c27a5aa0e4de5b95f50cf943bf5b71b9c4a81906407b5d3b6fe59cada698bad7930b1d93e98ead0b5d467f8a41c239c05f715172dabdacce8ce9a663815a53ffb1ba4770b98136adfaea685d5ab01b60acb0aceb21537b701d104f96edc2a60cab5e866c7686c57e1bac8632cac62832894efe0c075e2ae689a86b8346bd6f553a11824edccefc08b03cab4beac4cc10d77555ce484655a7ffea197f7bc153ca9c955e0485aee8dda93fd2f4eb7910b484fecb1481c82f651d41e4374c128268218fdb52258f257c4caf22f2337c6c62ec1f67c423e19a61687325897f25e249a3973456198ed516c52f17d306146c2fbb6822e5650e6212a19984d388718a745fe87bb127c0a4b390bb8c865513d2cfc0063c38a41f4aa015919016e00e21723b2593d4c7da4d18481405db785cc5f54ac637946c057b73e0cf0267782562a455606713df9ec9af4bac25c9b8854c15a4423062847691c691bb6305d978a3e1bc2743fcf09dd6c9cf6931e8a53c18f184e12f3b8c10f117f43664ee51db85b22857f5690c4b833439109d4effc166dd522dc0d2bcea10d401248d72aa8071b70e2bd9d5e022c3bacd91bf3438bd397d84eb1fc94fe884a3750be94330c28ae9a061f156b048dcc34683becc0cd9ce5ffbd2968bb4f1445f742212a0b49536cb2e8d03dd8af882ec87ad0d08f9681ced02a23889aa2396d133b2a13ef5a90a6d68e0f333b0d187eab74cefc1a58ed59e69742ea1cb0b7e4fbe79dca3daa73bc4af1c019d28b59071289a6c4f480141d274ebc0249f3105c61746b2208548022542ffdf697e78d93240bd63d13977cdf39ef936bf0f8a464dc7f9fb289b694ad4543f90b1338dbed53341053916cbb8f645a29524dcb865022db71e6565839f8ecee01ade85508634a27e58bca4c4d224b26a4c9b897514254ab0f1e297f48d063804d4dd60f63d08db985c0bf8852f8d8296eb92f2adb3e607bde0a4be5c7790dcadbf66e9c619345bce4122f5f359a4e81ae228648a0b0843819f9a12722104fa705b73730a66cb82b2f9512b22dce3cd8337029dd824bbf7dc3037efb28453ad2ef6b37d3e12021a834ca7bde41355661044575593234c76bb99f0e5216d735603faf966e7d44ea86d49fca2e31e613ce0eab7ba2e90c3f33978b15c9379f9f481e4a427d5b9bdc3f2e0ab5288a1f0b46e8e4c4af023f65c4d7ca13f0ec4bdd50821f90ab8806f5d33689de6aad54c4d9a8236023635164b6400199218e3dfb4ee7aa179987b1d4e4893a190962a881db6b91a0c3268f6354e9a06d8ff3789b242fc5b699879a9033ffab65ffc6e1bcbf96ee9ef41085b11fc362ce9df7c76ba2d66a0fcf10dfd14bbfe75498e8e0e87ff12ed1d4e9a250fca71e187109b7aa35b30eb38fd6252b35d2f5c3cdaf7e1f2311ea6ce6a6de6d1b681ede35078ac0a08cb79f74ba2fadd768b35ab789f83b9f9a5059e500de88a401d8e22e621f76c581a9f85b0d7ae39e58c078534ec76682067e0ab85f2aa6c9d46452c0f4058357aeee776b1eb0fe31615ae1a995dac73fc84068233b5b3d4d2c04b475a293c7ccfb6a2a6eb6e080a95e3ff88db38b60ba8127cd04abdd9055ba276e677488e1f5076c82ab2e194f0b7b369cac2261bfbc2dc749e5644e4cac3aec5db648707ebae08d1b43e1e08266b4b6c8d183b337f920868783cc194efa6d55bad9ccab2309e55426e95cae55c5bf20260389f052b6fc7d796d43a02bfc8f2978d39db4b4baa340d9eb4eb0b7aff432ca76552b5092c0cfc1ea55c702b2c5ac58579e1ecdad38ce68ed42f2f86d2fbe8f3cddd60b07a21d9099c6b08f22b97e1a022af285740932927bc88c834dfc33036ce86f095f5bba0bc4cb37d330acb59d6570a8e28297952cf4ed05134678605e420db31471b08cd6e4471637b8437f8880266f3dcfab80c22dc93b842abc923fbb12643f3ad3c68ec2ded37c1dba3eff45c9106db11535b14da39fc268f127d06a6a4da2c20090db3fb4cf35bcc74aa8627240b9fd9964939f984332217bcc00b579e87c912114ecaa2180b6d3c3a436e0ffaf9955d1707942930299d124a429d26c07bd349a0bb132bfdff861bd5bc5c15ce84abea3d040f77404c9ed1630f27cc11d1f885e56bd94703869989bfbd5247b0cb67243061a827a575b6958ba2ed809c55d75f898eb3074de5e490f43dcea4bd1e8019683b07f7270b3610e57837d57fb6a84e06ffedf589744b84e190a8e6407440d53c9ef7a50d179e2e273d35e37529b8ee1b3f0227f91cb1889cd25601fe685b858710da059cdfa4e84bd125529f866a349a22deda060d4a1eb59428ff5aa0bee006eb191650cbbebfe34b618bef661fae3411220328547c1fc2a1a6067e270d84d6246df36b516bf49979c37f8e7eaf8f38bc173685c76db015af86b5cd34825dcf70d3b54efa55e9163f25c1de29c1ff2a2a84ed008722c913343e727b6812216f4d0e0212359d5df3761129b4a95d8d7405e34017f60516d4c675df3f08a00e62f03010fc9d049ccf8b0a676c6f1603959e1202a6319706fcd28ea42d691283cafd925fe7cad83ded6f8e79ffea0704e84850d6f1d695e1cdbd335e11ac75abfc6ce977b28688242fef27de931c52732b6eb1d39020f49f5c5b34d9212765ae4077bd3f380c52e66552fe60a393607ea2f8c4cff65255e3a856f15b14def092b67083b0a9ab5ba31c9490e19266cd029c4b91f8c49365ae03a9494463a23a9effbedaf2c11592724098e357e50a2cbe3a1523fae9a7a045c773a578fd59c413d664d66a9cbb5927a237360184a0e0e36f0c274948a12c2aebfca648ddc0dee4164975b4cb0e5cfe686086197aabc88d6daa70fe992d48b86b224a8b123daec21352c2bf4b08664e5b90fe20dc7b378767306e9e86f570c82d53d025babbc546fc7094c067da9836e189140bd43734ef9834be3e25e235dd9a3a306045ac98a244c25c472dbc226505c8cd8da89f68641ec3932a18b720bbda0d072bbae4ae404cf4b8bb1f17f2eeee0a15cd61d53775909b0d1eb879422b87069afa96bbf82b938312b8231b9f29191c4c5983e3f596984494f3ac9c8bdf17316967be9ba0a9ebb299c17a9f411d9fb0f89385b0f5f1a2fa863b07a9a556c344da12da5807aeb7b685ec821d16e82e5c9b05e2cd79cf6c815fa66cac917b16fcd0c1178add6c01a15ffa534eaa3e9a87e58e5c9e76ac1cda1fe1b44fee4e7026a3931440ab20c977e8be1186b090dfcd054abb01018aef8047dd1d5b1fb0462b2ef5c6fb2050e685546cc0097db0e5d75dc09901adf32bd868b9176b60ca4aeaa3283decacc3c174029a2e51a94311087ca4b9d7d611df8f0f79c8cf917cee6573e7ca3d9ab7189ed95808e934e94b7047a22c8d070ced1a2748176b4d1eba8500a73ef3d858964143536734d45a9895a15977d2ec80df408da20b2d516620d0afa8c8e422888ca569f9382277363e2cdef3d853bbd05175bcaf16537c48102f473503ae8b87cd84165716203f6d5432b972fab50932da8932bea64f00065b1daf2bd1c1a70bd6236caf450b5172542fb1dfa9a0f15e7ea9de63c516a5ed937b2a6cf53db75817127c893184b7e2f2e12bad7b726ce8f06493e3c6324602c14ac03810febf2af50479aab291e570d1e81e68ecc0853d636497f885822423989ab8b8de5903914d0da7bcab87352ce9f88ad844097bd0a5ae40fb04c3194b72f997e7638607b30edb15d4a19648503f0449dd39a8a8360d017fff78c6e8c1e035edfbdb0fdac634ec05c09e1bea594fcfab009edeafa0593ee7c335eb381cc4950bacc0821b7334f92ecf40535f34d9edea363d8e1d57f6a911f3201a52a73da95931c06b2be6afa51e1d7602a3827a8a6f074fbbc2492f752eb8a59ba96365e39f281a22a59099e25a2fe6e3371747d733f9865a6509078ab2528bf74b196652e3fc5d884a0d198d02d51a60f705615679edc6cda6ddba41afacff5a776db11c965959cc9eed146fa5fc6882d10f271c96fd8c399debe6eb5d4ca153913c22e6d54cd9738d9dcbf1c8b91a3c2c2d912576e4e6e35cdf35c092202f772e7c33895f64fa899d2280f5206759029df5cbaefc2b61e40bd148c611a6a21728345b40bf6a2475bced235e48a34907fa47edbca39e8f9a0e0ece57b6bea14efe79649f75379e554c41d8c7017ae0c43495e5c5adbbe14875f3527638c7acc56266907f26aa8114d0f2c65188c2e913c003669b26ab3f22d4082198f9484772a4f3eb8f9d9d2936926642a413577c8a472c611a60d55bc30d2a7b74eb57f95ed83658528d15483386844674b42f8f20621c4c9b14d0c7c5aa6cda1c90fa904d6c5164320f0eac1b60592f1deebf47d5ac143865323931cc470fea2e060379629bc341c2d74abda1ec4e1bc8891c75f0e94cbb9f7588884f88b156f61dcb0419108923f63a85ce06db231c77dfd033b974513938644b7fd19f2bed75a4da4a5f3ce71830c1b806e814efea3ce26291740c46ce267e4d8a7e90ef6e34adb0a45e84abc76eedd3a4c40720c1a07c940472d386e637f6003e2252d1310a71f0ec0d3d43016e129228dabf9a06b473753d34229e37dc6d396e2256c50c9ff2278efa698edaf577cfa8befb102fe32672117e72deb6be1fa1e093685c425df5923e7ca597de7a2223b53210249af1ebed29afe7610b9b80c5a38b8a11b8e017bce930d00a387c1cecd7270ce7a1dbf2995f314f0c82eb4c872d03dd485a61380996f552d58e2c682f824f0b37b8e0ccf895e7365ea3830877d879354785c62329c4fec12b825b809ef42b04b9dceba2a88d5a20e2a2b23b8288057e9e7034ee34f1e8bc8f90b3edd5c0e73f9140d1802ac19cb0ec060d2c0670e116886b4bfd0bfbd8ec2b5258df08d6f0686fd0c0a1a32b6277a7cbee4997823d6327cf6e6a72dd0abdbfa90898a02b3c460dcf095adcae69b1791f319dec73568ea112497c7f861c437464a22893167be220a7a304c1cff622d8b606bd3068f7c01adc39fd127d97acd6474fba33123281f451cc76d36feb69566e599ac57b5d587be9b4894d2c382b93eb4faa4977d70e2397a773b72bc0cbafd92389e8cc9a8ecb70e531236929ed8b8d8909accb8aa00a3de64d8819a086b8d0fe0ef6ad491315cde572ccdf7119a13539eb6c7563356a29ef002cd70802e9110f770294c8255ef9ed86e612897a7de2ff2f27a5e7c8d078145907a75056b0ccfccfbdd29cc8f111b1c2863e3eb6904b02bfc62156ecce51e3a54cbb5667387cbf8ba3b4b15e4f8032944ba0638df2e0dda6713824282fcaf4684c49b2081fdc82a707a93bd62a1c679c22199e5c1253d13c6fc89483f5abc76564e658b33245e7035b464a1ee48c5a38fbbd6c5017c91de16088a74018a8ce2e5521f262117179c9e71300f789164581f2bf33f9a618d9245b5afbfc2e0be97a2fb86e2f228289d191544c286f2ccaeffe54263c41af856699643b225b0574a6addb63cc6c9d6607fc4dfe4af97630873b6bde63c2ac39c0f2a82a1040ed69cc759414bfa67791ba6b477b2e9e9114ac23c5aa921f27d594629d55f2cb9ef4160fadeec9a3e8dc1a2c6b1569fcac431e447e1c89899c8530b972cb8d6846d0ebdd04dd74bec66404854b21d95ca506240caf063b20b3c677a94337b7ecacbe108d058b1ea79be9682f1556475626b6f9269bf3c39fe16665cb4c3ba0f26eae37634f9b19e91854687e9262ee86f4a0662716f4d4ce07fd57c0bd79313eb8eeb1a8db89a545b29f862abdff05175df4601c0ce84f4e33edeceb8e15d919227ccd57421bd41c6ee5a480cca27b34350654192341161fc89b54fa2c79f0c9c1c6a998cddaa76030984a3854f94e93c92bf2ecb1278aa5a19b6c26ffb2efb9e31ac9ab7d85316a2d01c1737eae5b759ac378e7a49ddd97a440b8499fb65dc57ca7e253f41ee9147f96c35d318dcc50cc766b6d6e2a48e0849051bcc5b2bdf65f060218a0fd8aa3634315ffd1f49473d06e6391aa358279a55f34dcaa68ab6b6c1fbade2320c41ec6249a49de647872d9e80056afc82d2292e2745699cfa499de75538dd0f742e97b9ec0e1fe7f6de187216707865239cd7f98dfde79a673bf8f5710bfde505b36377a775c86a639b54f7a9257ef3aaea5ddc9be80419c53e81542c12a3458b6f90c60c2e25d0ec44b87ecf7e10da4bbbe6a160d84a155007e89fa119fd813495a87c058ed945434aa2ace46bf0d23965c0cc2ca2e3a304006d6a0995ec29d27b6058c06b7392d0ee45c4f0cfc2da2f3f528588cf9777f426a53c59a9c259b5af30853d0b4b55151e0c48e81122c7c517832053ff141b0eb8fdd32df3bd8d6deb43f16d5a0309a8b591b553972298e2bf7acd1881b5f8280c14ceb3045f3938e07c898e8849b4da4ea177ed61b9bc6b91b9f1ab3ff0676dff99ab77313457e44792de597e7d6df62addfff4a341eabf9926ebbfb55b5a7b3118aa3730d48334ebec98dcf8c243c283d1a726407fc70ffc0eb268fe4409167da69df498ea17c9e35cdeaaad0033cb18ed62cc1dc5cbcd13d076a34d551096839b9d8e4db072c95a2149de3a18eea765155f69e927cc7ccebafae320afb26f4fc4a2564c593a52c3db150df73939ff164bfed288fd442cece64a07e55b78bcae2e2586263c1280aeb4f8b1659433ba93fb452c0f66a795af2693a0009d336ab8864ad4ec5c727f05e44d039e7e4fa29e5c6e46340cc37bd380b522512969e88dfbb09ab1c63fb941b43c0085511c13cb9be6598fb83b4ada5067b347149e15f7c227d77721a736d92f52fbfa82fd83e7211ce2865845c2b2718c25510724d9fdbc6d9d41b01ff70804e5b8e6286d5a0135e256d8725eb32b95c1582f0f296fe3296453e4407d72d474d459bfa4d3a2852e1289f8a7d1a04ef42cddc1118529dc5661589be8fef4a284c80e589d85f645984a3fee953c374de48b017f43874ece7c54b6e5bfc6307a5231cafe2f5191ae5c136876427542f73439cf2aa69f521c69cab2ec89bcd6a290c594de1d951396d36f9162daabd3c789ffaf8527c39dd66697a1592ab8af20dd9b0da37295615bc0ec7fec67825878abbe94b17e288adefe009cd89301a3a9c2668b26cceca6321afaafaeb543a2593f7ebaf5954352c1987b5b9db451e526fa4d83f122f996383b3b582977faf402c393cbbfa45581eea8957ea3da64d49d94157ffba4169d2362aa9b472180d737bc0a7e64efd1dbb8f2aef1949cac0f01b0c9d31c8c27054d07770b7a3c3d070caa256fa7f926bf59dfc4a1a1bdba630ce72e103e18292b5dfef65d6bc7d8e118296ee2c0a6db412662bbf353948b9ba88cc8339ba90370ba7316b33069ed08d822926cb53da23262087a7ed64f6bde2b73c2dc3c538a94c3b9a3a163ac85a181c7e8866fc19052474076a8a6a6af50fed2065554619f2fc0a38653d941d9fb4df3b74ab6e877baddd9f8cb915cf3b2b899960190c7f8caf3ec6c7ccb3e7cbe2bc6547c4908a49db4414c3e07c086309a2e4676ba79d310f283a38c1f14534ff39e6c50c7a218f60396dc5e5afb08f19121bd8a92ba9e8d96b7f5dd8ec5a265f4da039aa1bc76f4b09fd21886062959db554d28f328cc5e1d2ce22f055b1d105a52336d0e7b8e6e26c3d872d9ca713f51196e2e0cd1d1c33dd933115579c484097dd40f1d6150a9d0b95d1c227e763c3e302c1fefd3f656f96aa4c63ad340b5b4380513c30531f6113adc5b5b48483f9239a8f8686d875bb06d190237fa3d92928c941fc1384317b35e902f5b04d0cc5f58c8d4d9a96dc68e4240487b1fd01208b35d2610e5d7910f909eb822280b385947f38bfaf1729424e4b4ec108254cba47e7cd8ea55ca82dec86b0c32235c689748e29adf0ffe8e04dc823a67a1bd220b9849cf760d3226d4703fe4da970795d138a8be506d867c7ad0d081a22ec0bf01a46a6df532b48dfd93ae2df01bb8e67360e1c3b08caa8674af3d01cc044a8da821ff2e3b8e595e7ac4c16330e00dd0ebac867feeb40c8080e2a02a42517fde15fae309e76a664c3aa30ddcd78376089ff5e941e302b272411c2a013c492c1c64dea69175230720d8e954e45b91c83ff48269291e678fe705fa95e6f49d5eed7b32f6a5cfe22ac0dfaff9d88c8326ef6b07274815bd7f09be16969d660dbc8e4e117b1d4c9e0d06b842635fa1ad7a27ef3f8e7451f1e36397f2bd7c9e5f46702bfd2777b91efe9e560b38d3544812b8a40909dba0d18ce662dcf3db27e9fb38b056977adc77a39884273123937e5e8c5131b4cb587dec634e8bd0c599903683e689ba68d6a48ea0e052177eab58464407ec2cc3d8049928b1b605fe71b55f693f9cef7658d8663519c05d3446280c059b9fe898ecbadbf763e346b469a22ad95fcbd67cd50faecacccb346aa5ebeb05d79a91f796a2e1b93539fa7a45aea2435fc301ffeb0ca21220b0fcb62c0cb48f1d113034a471a8bad131e3701176957f828aecb0f9b5fe23276ba9f48e9f3edcf74e79230189afc6c05c077047f4d7512f978a4a72e7e300bbaf0f1d7472c6ca9afce885827f3092d305384baf9962e6272628d21b1e41994f7ed5dce24388f210a2887d103abb1a13456bd8668bd924a3b30abb678b9816b82af7e956b64f5556b72c38b3cb1ce5a83e5952b18f42e34e561a57ff95d7ea53dca061a724bf8a4a72a253695d1aa90682b41f51827716a3354cf8744b159d77ac59588cb85d55a07114ec002d14e5a9e7769dd75fbd52b36ded9d6391111ee24c8aea92b1d6934efe840cf65258652ee471ad64a648c286775cb5d1bccea27dcd5c01c31eae56ccc18b197aa409818c49b961c8920094528f49b8f62d12c1ca37fed2b69b47d9829d182c1ab6bfa01ae420921873da71f13e3f87d2e445e479cbd18b7d3b6997a2a2cd9c8bbd632692367847795bc8532e5bbb677bf89c060362ec61582d121f4dadfe9c8da63f89a99493f380a611718835b6d3b07663a35a467a4b8c9c2032757dfcd169b0e1b59096286a426e6d1e35f4371ddd415e1d7d1cf101e5d9eaa1be9d0727c764b061f67a304a1279aac6a8dc66bc65149076b73753677ced828d0075e1739eaff6edb59b99e8bc3c62a200566d08da84065127a5eb1ce6e9b9320b2d3ef42d33c07fa4d765c29871fe552466bff07f7f204232aafb81c9d4780938b0d969bf8720054abcb8d445d44ec8c2243cc8de44b82024374aa6852994678f2d38cfff9cac17561e6fb44540a4c6ab2fb0d4b2046134b2b4ccf4ba8c6006d4245857fdc8a72209e5fd6c667514bc03350af68aff6b591eeaa1f2a8bc49969681d6e9d10d25933c507a9d93a535c3a5e306958d3a23c14607a2c64cad2c3a74fc851e596e416e35c55d1e0263a2e6d57697250828c21b8dc015cb11ecd026e5e32375e03128df4ff32789003e16f88513a3ef9d6174d93e2a011af1a0809a3c0d13a3f88c107fa8601ed05134a4eb1c3f636f371e12a3763045eaf1f3c41ca99804c7987ced3c77d6f93f1e9d46aa4cb4035ec681b3e8824b8649f32575d46f1eb98d86a23200ed92454ec1b079947439b1bc2d20104e3684aaefe2251891b2da54f3354dd08fbc2ba537f35c85e2e5217a2ddd69e522704b1c9dc726ac1e35ae57deb529a38fa572c7ace3f0cacbf3fc103f213bd82319376cf28e055ad28900922396b465beaed7203ccf4699e937a3c997639c25a9cfef601bdb6b34c5e56cd0f586a20fc2e75bbc49492220bd7ca9e5e95d19afbe7a923e02e81f024c2aec62f7ad986d67f637dee4108658c663abbeec8de7c3457533f023222440cb7c7b1c3a3a22feb2c639b954238074dea7c9da9fd43b85a3c3a80635da520ab8810251a840143d9a31564912408efe5faa2db327b1abf2001d3c0d82df8c63afe6cae386820abac516a295f0fb0275cfbad22fdc3dfa09150f1f24f26878292c6015bae8a24fbdf2e4a012736f7d02a0fe11bac9826fb26e012485b476cb006de9680176d16f3ea9c5e73ebe8607f0f77bc406374948893913d128d2ab3831e65002019f67c49de1bda9d24f9236ca377c113329a2a0aaf72074d8a1170ac9fc72860bea7ccb2c7aa10aac3e4f847218564b333a0f83bcd751bf006328c061ce8efda3bed6a5febd16be95cf955b1463177264aa14c2ef20e2a98b1784a3d82a2b65b5fb0960558b942c7abee33b4c1eae8ec37254224e6118c943e724db9b414312242752de2d9dcbd8475e2d8c3914964c73dc4cceabceb357a2d96790e678451364230425295b7cc3b1b20f3dc92daf4203f1b5bb58e115d2c97f1508db724e4ebb629ebb73dd5e0913a76f2dc16274546dc3453ace2889935af52d947cf91b72e8d943909cf0b44369e68222586755533b" ) ================================================ FILE: pset02/README.md ================================================ # pset02 : NameChain Mine your name into the blockchain. This pset will use a mock blockchain which you can mine blocks into. Due on the last second of the month of February: 2018-02-28 23:59:59 EST # Warning: centralized server There is a server component to this pset, which might go down or crash. This is not a decentralized network hardened by 9 years of open source improvements and worth hundreds of billions of dollars. It's a server I coded just for this pset. If it goes down, bug me on IRC and I'll try to fix it quickly so that you can increase your mining score. The server, at hubris.media.mit.edu, should be up and running from the 20th to the 28th. Server is back up on 2018-02-22 10:45 EST ## Block specifications Blocks are ASCII strings, with a maximum lenght of 100 characters. The block format is: prevhash name nonce prevhash: the ascii hexidecimal representation of the previous block hash. Must be lowercase and in hex; do not use raw bytes. example: 00000000c80b6fe911b091a7c05124b64eeece964e09c058ef8f9805daca546b name: the name you want to credit nameChain points to. Case sensitive. example: miner2049 nonce: a random nonce to satisfy the work requirement. example: TWFuI3GlzIGR Note that names and nonces must be in the base64 character set: ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ which is just caps, lowercase, numbers and + and /. Can't have spaces; spaces separate the 3 different elements of the block. This is to make everthing easy to run through terminals, let people use shell scripts and so on. ## Client / Server connections This pset has a server. It's not a real decentralized system, as that's too much work to deal with for this early assignment. There is a NameChain server which listens on a TCP port, and when a TCP connection is made to it, it sends the current blockchain tip. Connected clients can send a new block to it, which, if valid, will be appended to the end of the blockchain. The server is hard-coded into the pset code and lives at hubris.media.mit.edu. It will probably go down for some time during this pset epoch, due to the fact that the DCI is moving to a new office this week. We'll try to minimize downtime. The required work is 2^33, which is twice as difficult as the initial target for the Bitcoin network. (But nowhere near as difficult as the current Bitcoin target) ## What to do: A bunch is already written for you. The network functions are GetTipFromServer() and SendBlockToServer() and already implemented, so you don't have to deal with TCP. You need to write the Mine() function, and then SendBlockToServer() once you have mined a block. You may need to poll the server for new blocks occasionally so that you don't submit "stale" blocks, where someone else submitted a block before you did with the same parent. To not DDoS our server, please keep requests to 1 per second maximum. Using go concurrency features is reccomended for this problem set. The amount of work required to find a block is fairly high, and if you're mining using only 1 CPU core you will be at a disadvantage compared to multi-threaded, multi-core miners. Here is a [simple tutorial](https://gobyexample.com/channels) on go channels; they're not too hard to use, even if you haven't done multi-threaded programming before. They allow you to pass messages between functions which are running at the same time. There are also other possible methods like sync.WaitGroup, or in fact it's quite possible to mine without any synchronization between threads at all. There will be a "high score" ranking with the number of blocks in the chain made by each user. This is the same as getting more coins by mining more blocks. To get a high score, mine a bunch of blocks with the same name. We're using names instead of public keys which is not as decentralized but we know who you are already :) The entire current blockchain can be downloaded from the server at port 6299. Example code: ``` $ nc hubris.media.mit.edu 6299 00000000722a3b3cabaac078bd4e15ce361312895cfef0494c9ffc75bedb82db adiabat 19579781213 ``` This if if you want to check how the blockchain is progressing. If there are other server features wanted, let me know and I can code them up. The server code is not public now, but I'll make it public after the assignment is over. Having a server makes this pset somewhat more fragile so bear with us :) We hope this assignment is fun and people can optimize and speed up their code, while getting the basics of blockchain design. ================================================ FILE: pset02/client.go ================================================ package main import ( "bufio" "fmt" "net" ) var ( // note that this server is not up yet! Will be soon! serverHostname = "hubris.media.mit.edu:6262" // uncomment for testing & running a server on localhost // serverHostname = "127.0.0.1:6262" ) // The functions in this file are provided to give connectivity to the // NameChain server. They aren't really the best way to transmit & receive // data over the internet, but it works OK, and we're not focused on the // network layer right now. You shouldn't have to modify this code but // do let me know if it's not working properly. // GetTipFromServer connects to the server and gets the tip of the blockchain. // Can return an error if the connection doesn't work or the server is sending // invalid data that doesn't look like a block. func GetTipFromServer() (Block, error) { var bl Block connection, err := net.Dial("tcp", serverHostname) if err != nil { return bl, err } fmt.Printf("connected to server %s\n", connection.RemoteAddr().String()) // send tip request to server sendbytes := []byte("TRQ\n") // write to server, error out if needed _, err = connection.Write(sendbytes) if err != nil { return bl, err } // setup to read response bufReader := bufio.NewReader(connection) // read from TCP blockLine, err := bufReader.ReadBytes('\n') if err != nil { return bl, err } fmt.Printf("read from server:\n%s\n", string(blockLine)) // convert to block bl, err = BlockFromString(string(blockLine)) if err != nil { return bl, err } // return block return bl, nil } // SendBlockToServer connects to the server and sends a block. The server won't // respond at all! :( But you can check for the tip by connecting again to see // if the server updated it's blockchain func SendBlockToServer(bl Block) error { connection, err := net.Dial("tcp", serverHostname) if err != nil { return err } fmt.Printf("connected to server %s\n", connection.RemoteAddr().String()) // Server will send us data but we can ignore it and just send // use newline to indicate end of transmission. A bit ugly but OK. sendbytes := []byte(fmt.Sprintf("%s\n", bl.ToString())) _, err = connection.Write(sendbytes) if err != nil { return err } // read response from server and print out. bufReader := bufio.NewReader(connection) ResponseLine, err := bufReader.ReadBytes('\n') if err != nil { return err } fmt.Printf("Server resposnse: %s\n", string(ResponseLine)) return connection.Close() } ================================================ FILE: pset02/main.go ================================================ package main import ( "crypto/sha256" "encoding/hex" "fmt" "strings" ) // A hash is a sha256 hash, as in pset01 type Hash [32]byte // ToString gives you a hex string of the hash func (self Hash) ToString() string { return fmt.Sprintf("%x", self) } // Blocks are what make the chain in this pset; different than just a 32 byte array // from last time. Has a previous block hash, a name and a nonce. type Block struct { PrevHash Hash Name string Nonce string } // ToString turns a block into an ascii string which can be sent over the // network or printed to the screen. func (self Block) ToString() string { return fmt.Sprintf("%x %s %s", self.PrevHash, self.Name, self.Nonce) } // Hash returns the sha256 hash of the block. Hopefully starts with zeros! func (self Block) Hash() Hash { return sha256.Sum256([]byte(self.ToString())) } // BlockFromString takes in a string and converts it to a block, if possible func BlockFromString(s string) (Block, error) { var bl Block // check string length if len(s) < 66 || len(s) > 100 { return bl, fmt.Errorf("Invalid string length %d, expect 66 to 100", len(s)) } // split into 3 substrings via spaces subStrings := strings.Split(s, " ") if len(subStrings) != 3 { return bl, fmt.Errorf("got %d elements, expect 3", len(subStrings)) } hashbytes, err := hex.DecodeString(subStrings[0]) if err != nil { return bl, err } if len(hashbytes) != 32 { return bl, fmt.Errorf("got %d byte hash, expect 32", len(hashbytes)) } copy(bl.PrevHash[:], hashbytes) bl.Name = subStrings[1] // remove trailing newline if there; the blocks don't include newlines, but // when transmitted over TCP there's a newline to signal end of block bl.Nonce = strings.TrimSpace(subStrings[2]) // TODO add more checks on name/nonce ...? return bl, nil } func main() { fmt.Printf("NameChain Miner v0.1\n") // Your code here! // Basic idea: // Get tip from server, mine a block pointing to that tip, // then submit to server. // To reduce stales, poll the server every so often and update the // tip you're mining off of if it has changed. return } ================================================ FILE: pset02/miner.go ================================================ package main // This file is for the mining code. // Note that "targetBits" for this assignment, at least initially, is 33. // This could change during the assignment duration! I will post if it does. // Mine mines a block by varying the nonce until the hash has targetBits 0s in // the beginning. Could take forever if targetBits is too high. // Modifies a block in place by using a pointer receiver. func (self *Block) Mine(targetBits uint8) { // your mining code here // also feel free to get rid of this method entirely if you want to // organize things a different way; this is just a suggestion return } // CheckWork checks if there's enough work func CheckWork(bl Block, targetBits uint8) bool { // your checkwork code here // feel free to inline this or do something else. I just did it this way // so I'm giving empty functions here. return false } ================================================ FILE: pset02/server/README.md ================================================ ## Server code for pset02 This is a bare bones server that pset02 can connect to ================================================ FILE: pset02/server/server.go ================================================ package main import ( "bufio" "fmt" "io/ioutil" "log" "net" "os" "sort" "strings" "sync" ) var ( genesisBlock = "0000000000000000000000000000000000000000000000000000000000000000 satoshi 11970128322" chainFilename = "./chain.txt" chainOldFilename = "./chainreload.txt" ) // BlockChain is not actually a blockchain, it's just the tip. // The chain itself only exists in a file. type BlockChain struct { mtx sync.Mutex tip Block bchan chan Block } func Server() error { f2, err := os.Create(chainFilename) if err != nil { return err } f2.Close() var bc BlockChain // initialize channel bc.bchan = make(chan Block, 8) // Ignore errors here; it's hard-coded // this is kindof ugly as it doesn't show up anywhere so it's like height // negative 1. Weird but whatever. bc.tip, _ = BlockFromString(genesisBlock) // start handler routine for accepting new blocks from clients go HandleBlockSubmission(&bc) // handler is running, so feed it blocks from disk err = LoadChain(&bc) if err != nil { return err } serverListener, err := net.Listen("tcp", ":6262") if err != nil { return err } hiscoreListener, err := net.Listen("tcp", ":6299") if err != nil { return err } go ServeHiScores(hiscoreListener) for { // blocks here serverConnection, err := serverListener.Accept() if err != nil { return err } go HandleServerConnection(serverConnection, &bc) } return nil } type Score struct { name string points uint32 } type ScoreList []Score func (p ScoreList) Len() int { return len(p) } func (p ScoreList) Less(i, j int) bool { return p[i].points < p[j].points } func (p ScoreList) Swap(i, j int) { p[i], p[j] = p[j], p[i] } // ServeHiScores listens for incoming TCP connections and sends them the high // scores of the blockchain. func ServeHiScores(l net.Listener) { for { hiCon, err := l.Accept() if err != nil { log.Printf("Hi score server error: %s\n", err.Error()) } chain, err := ioutil.ReadFile(chainFilename) if err != nil { log.Printf("Hi score server error: %s\n", err.Error()) } lines := strings.Split(string(chain), "\n") scoreMap := make(map[string]uint32) // populate map with scores for _, line := range lines { // assume all block strings are good, so name is [1] vals := strings.Split(line, " ") if len(vals) < 3 { continue } _, valid := scoreMap[vals[1]] if !valid { scoreMap[vals[1]] = 1 } else { scoreMap[vals[1]]++ } } // make sortable score slice sl := make(ScoreList, len(scoreMap)) // populate slice var i uint32 for k, v := range scoreMap { sl[i] = Score{k, v} i++ } // sort slice sort.Sort(sort.Reverse(sl)) scoreReply := fmt.Sprintf("--- pset02 high score list ---\n") scoreReply += fmt.Sprintf("rank\tpoints\tname\n") // print in order to string for i, sc := range sl { scoreReply += fmt.Sprintf("#%d\t%d\t%s\n", i, sc.points, sc.name) } recentReply := fmt.Sprintf("--- most recent blocks ---\n") for i := len(lines) - 10; i < len(lines); i++ { recentReply += lines[i] + "\n" } _, err = hiCon.Write([]byte(scoreReply + recentReply)) if err != nil { log.Printf("Hi score server error: %s\n", err.Error()) } hiCon.Close() } } // LoadChain reloads an old chain from disk func LoadChain(bc *BlockChain) error { f, err := os.OpenFile(chainOldFilename, os.O_RDONLY, 0666) if err != nil { return err } scanner := bufio.NewScanner(f) for scanner.Scan() { blockLine := scanner.Text() if err != nil { return err } newBl, err := BlockFromString(string(blockLine)) if err != nil { return err } // submit block to handler routine bc.bchan <- newBl } return nil } /* Server protocol: listen for command. Commands are "TRQ" or a block. Respond to TRQ with tip block. Respond to block with ACK message accepting block hash, or error. Respond to any other command with "Unknown command" */ // Handle the connection from clients. Concurrent goroutine, so there // can be a bunch of these happening at once func HandleServerConnection(connection net.Conn, bc *BlockChain) { log.Printf("Incoming connection from %s\n", connection.RemoteAddr().String()) // First, read from them bufReader := bufio.NewReader(connection) blockLine, err := bufReader.ReadBytes('\n') if err != nil { log.Printf("TCP error: %s\n", err.Error()) } // sendBytes is whatever we're going to send them var sendBytes []byte // Now detect if it's a tip request if strings.HasPrefix(string(blockLine), "TRQ") { // ready tip for sending // lock mutex, get the string to send, and unlock bc.mtx.Lock() sendString := bc.tip.ToString() bc.mtx.Unlock() // use newline to indicate end of transmission. A bit ugly but OK. sendBytes = []byte(fmt.Sprintf("%s\n", sendString)) } else { // interpret as block submission newBl, err := BlockFromString(string(blockLine)) if err != nil { // neither TRQ nor block, send error message sendBytes = []byte(fmt.Sprintf( "Malformed block error: %s\n", err.Error())) } else { // no error, absorb block // first check if it will append if !CheckNextBlock(bc.tip, newBl) { sendBytes = []byte(fmt.Sprintf( "Block invalid: not enough work, or doesn't connect to tip\n")) } else { sendBytes = []byte(fmt.Sprintf( "Block accepted\n")) } // submit block to handler routine bc.bchan <- newBl } } _, err = connection.Write(sendBytes) if err != nil { log.Printf("TCP error: %s\n", err.Error()) } // disconnect client connection.Close() return } // HandleBlockSubmission checks that the block is OK and fits on the end of the // chain, then adds it on at the end. func HandleBlockSubmission(bc *BlockChain) { var blockHistory []Block // loop forever for { proposedBlock := <-bc.bchan log.Printf("got hash %x\n", proposedBlock.Hash()) // TODO actually check stuff // Pretty sure I don't need a mutex here because I'm just reading // and this function is the only thing that can write. Also there's // only 1 iteration of this loop happening at a time. if !CheckNextBlock(bc.tip, proposedBlock) { log.Printf("Invalid block received:\n%s\n", proposedBlock.ToString()) continue } // this thing keeps history, maybe save to disk... blockHistory = append(blockHistory, proposedBlock) // use mutex and update tip // also append to file bc.mtx.Lock() bc.tip = proposedBlock f, err := os.OpenFile(chainFilename, os.O_APPEND|os.O_WRONLY, 0666) if err != nil { // crash if file doesn't work panic(err) } f.WriteString(proposedBlock.ToString() + "\n") f.Close() bc.mtx.Unlock() log.Printf("Block accepted; height is now %d\n", len(blockHistory)) } } // CheckNextBlock checks if the block attaches, has work, etc. // Assumes "prev" block is OK, but checks "next" func CheckNextBlock(prev, next Block) bool { // first check the work on the new block. 33 bits needed. if !CheckWork(next, 33) { log.Printf("not enought work! ") return false } // first, check that next points back to prev if prev.Hash() != next.PrevHash { log.Printf("hashes don't chain up! ") return false } // probably enough checks for now? return true } // CheckWork checks if there's enough work func CheckWork(bl Block, targetBits uint8) bool { h := bl.Hash() for i := uint8(0); i < targetBits; i++ { // for every bit from the MSB down, check if it's a 1. // If it is, stop and fail. // Could definitely speed this up by checking bytes at a time. // Left as excercise for the reader...? if (h[i/8]>>(7-(i%8)))&0x01 == 1 { return false } } return true } ================================================ FILE: pset03/README.md ================================================ # pset03 In this assignment, we'll make some transactions on the bitcoin (test) network. For pset03, we're going to make some transactions on the Bitcoin test network. This assignment uses btcd for libraries, which is a bitcoin implementation written in golang. The goal is to understand how transactions are constructed and signed, and to become more familiar with the utxo model bitcoin uses. Testnet3 is a network for testing out bitcoin. It works almost exactly like the regular bitcoin network (small changes to addresses, the difficulty of proof of work) but everyone agrees that the testnet coins are not worth anything. This isn't enforced by anything on the network, it's just something people decide. The fact that it's testnet3 indicates that this rule failed for testnets 1 and 2, when people started trading the testnet coins for mainnet coins. In this pset you'll perform many of the functions of wallet software, by identifying outputs to spend, creating transactions, signing them, and broadcasting them to the network. Most wallet software does this all automatically, but this assignment is more manual so you can see how it works. ## Setup Get bitcoin core 0.16.0, available at https://bitcoincore.org/en/download/ To get on to the test network, make a bitcoin.conf file in your bitcoin folder (which is HOMEDIR/.bitcoin/bitcoin.conf) (In linux that's ~/.bitcoin/) and have the following line in the conf file: testnet=1 and then run bitcoind --daemon so that it runs in the background. Syncing up to the testnet will require download of around 12 GB, and will take a few hours depending on your computer's speed. Some MIT guest wifi will block outgoing connections to different ports, so try using wired ethernet or another SSID if it doesn't seem to download. Once bitcoind is running, you can see what it's doing by looking at the /.bitcoin/testnet3/debug.log file, and issue commands with bitcoin-cli. In this repo there are 4 files: ``` main.go addrfrompriv.go eztxbuilder.go opreturn.go ``` Here's what they do #### main.go This is the main function which is called when you run `./utxohunt` Edit this file to call functions from other files when you run the program. #### addrfrompriv.go Creates a public key and bitcoin address from a private key. Addresses are copy&pastable encodings of public key hashes. #### eztxbuilder.go Puts a transaction together, signs it, and prints the tx hex to the screen. This can then be sent to the network with the pushrawtransaction command in bitcoin-cli like , or to your own bitcoin node with `./bitcoin-cli pushrawtransaction (tx hex)` [https://testnet.smartbit.com.au/txs/pushtx](https://testnet.smartbit.com.au/txs/pushtx) #### opreturn.go Similar to eztxbuilder.go, but creates a transaction with 1 input, and 2 outputs. 1 of the outputs is an "OP_RETURN" output which can contain arbitrary data. Use this to submit your results to the blockchain. ## Task 1: Create a Bitcoin Address First, look in utxohunt/main.go, and make a keypair. The AddressFromPrivateKey() function will help you. Put your own random string in to generate a private key. If you call the AddressFromPrivateKey() function, it will return that address as a string, as well as give you the compressed public key and pay to witness pubkey hash script. Save this address (it starts with an "m"). You'll need this to send the money to yourself. ## Task 2: Find the first treasure hunt transaction A _block explorer_ is a website which watches the blockchain and parses out information about blocks, addresses, and transactions. You can use this blockexplorer to see what's happening on the Bitcoin testnet: [https://testnet.smartbit.com.au/](https://testnet.smartbit.com.au/). I've created a transaction with one 70 outputs. `1f497ac245eb25cd94157c290f62d042e3bdda1e57920b6d1d2c5cfa362c12da` is the txid, or unique identifier of this transaction. (The txid is the hash of the serialized transaction) The outputs of this transaction are all have the same address, which determines how they can be spent. The private key for this pubkey-hash address is the double-sha256 of the string "mas.s62". Claim an unspent output in this transaction. Please be nice and leave the rest of the outputs for other classmates! :) ## Task 3: Make a transaction Using EZTxBuilder(), make a transaction sending from the up-for-grabs transaction to your own address. You will need to modify hashStr outPoint (output index number) sendToAddressString prevAddressString (the address of the "BTC secret key" pubkey) wire.NewTxOut (change the amount to less than the input amount. A few thousand less is enough of a fee) When you modify the code, you need to re-compile the code. Run "go build" in that directory to compile. You'll get a long hex string which you can test by running the transaction though bitcoin-cli's decoderawtransaction command `./bitcoin-cli decoderawtransaction (tx hex)` If you get an error, it might be for one of the following reasons: 1. Someone has already claimed the output you are trying to get. Go back and look at the transaction's page and see if the output is still available. It will say "inputs spent" or equivalent. 2. 64: non-mandatory-script-verify-flag (Signature must be zero for failed CHECK(MULTI)SIG operation). This means your signature was invalid. Often this is because the hash being signed was invalid. This could be because the previous output you signed and the one you indicted don't match, the wrong amount is being sent to the WitnessScript function, or some other invalid data is in the transaction prior to signing. An invalid signature can also be caused by using the wrong key. In that case, you will usually get this error: 3. 64: non-mandatory-script-verify-flag (Script failed an OP_EQUALVERIFY operation). This means you're probably using the wrong key to sign with, as the public key used and public key hash in the previous output script don't match. 4. TX decode failed. That means you're missing some characters, or the transaction is otherwise unintelligable to the bitcoin-cli parser. If everything worked, the decoderawtransaction output will show you a json representation of the transaction you've created. You can then send it to the network with the command sendrawtransaction. If that works, it will return a txid. If that works and the transaction is confirmed (chechk with getrawtransaction), you've got some testnet coins! You can use the same EZTxBuilder() to send that money somewhere else. ## Further steps / bonus money Try to get some more money. There are some coins stashed through the network, and I will add more over the week :) The first output One has a private key which is the double-sha256 of the *address* from which you took the first coins. To grab these coins, you will need to use AddressFromPrivateKey() to generate that address, search the blockchain for the txid, and try to send an output to yourself, the same way as with the first transaction you created. [More coins for grabs will be added soon -- check back when this document is updated on github!] Note that in many cases, someone else in the class may have grabbed the coins before you. That's OK, just write down where you found the coins to be and the private key you would have used to take them. ## Submitting work Submit your homework... ON THE BLOCKCHAIN! (note that you should also submit your code to the class github; the OP_RETURN is a fun way to try out this aspect, and everyone can see it) The opreturn.go file will walk you through how to make an OP_RETURN transaction. These transactions are on the public blockchain, and we'll find them there. OP_RETURN outputs start with a single opcode (OP_RETURN) which terminates script execution. This output can never be spent, and is thus not added to the utxo database. You can put whatever data you want after the OP_RETURN, though it's limited to 40 bytes in length. For this assignment, sending your coins to an OP_RETURN output with your name or MIT ID number is cryptographic proof that you sent the coins (or someone else did, impersonating you!) Use opreturn.go to create transactions spending the outputs you sent to yourself using EZTxBuilder(). The created transaction will to an address as well as an OP_RETURN output. Broadcast this to the network, hope it gets into a block, and you're done! To parse other OP_RETURNs, or the one you made yourself, try using python. Here's an example transaction: `c29dc7b974901989c156578fc8dd341752bf28e415191bb1dc4b3aabc3ac11c5` the OP_RETURN is 363839322054657374206f7574707574 Load up python in your terminal (most linux and mac terminals have it) by running ` $ python ` from there: ``` >>> "363839322054657374206f7574707574".decode("hex") '6892 Test output' ``` Prefix all your OP_RETURNs with s62 so it's easy to search for them. If you only grab a little bit of money and send an OP_RETURN, that's fine. If you manage to get some of the bonus utxos and send OP_RETURNs, even better! If you want to get really fancy, try aggregating all your outputs into a single, higher value tx output. (Code left as excercise to the reader) ================================================ FILE: pset03/addrfrompriv.go ================================================ package main import ( "fmt" "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcutil" ) func AddressFromPrivateKey() (string, error) { // private key is the hash of some string (better to use real randomness // or a real KDF but this is OK for class. // Put any phrase you want here to make your own private key. phraseHash := chainhash.DoubleHashB([]byte("mpQQryVrYmGNPxVqNeE5RgoYAv2v66Psao")) // make a new private key struct. Private key structs also have a pubkey in them priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), phraseHash) // print out what it looks like in hex, compressed (x-coordinate, y-sign only) fmt.Printf("pubkey is %x\n", priv.PubKey().SerializeCompressed()) hash160 := btcutil.Hash160(priv.PubKey().SerializeCompressed()) adr, err := btcutil.NewAddressPubKeyHash(hash160, testnet3Parameters) if err != nil { return "", err } script, err := txscript.PayToAddrScript(adr) if err != nil { return "", err } fmt.Printf("script is: %x\n", script) return adr.String(), nil } ================================================ FILE: pset03/eztxbuilder.go ================================================ package main import ( "bytes" "fmt" "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) // TxToHex takes a transaction and outputs the serialized tx in hex. // Provided to make things easier. Returns an empty string if there's an error. func TxToHex(tx *wire.MsgTx) string { if tx == nil { return "" } buf := new(bytes.Buffer) tx.Serialize(buf) return fmt.Sprintf("%x", buf.Bytes()) } func EZTxBuilder() *wire.MsgTx { // create a new, empty transaction, set version to 2 tx := wire.NewMsgTx(2) // we need to add at least one input and one output. Lets build the input first // inputs consist of a previous output point, and a witness (signature data) // output points (out points) are a transaction hash (txid) and an index number // indicating which output for that transaction is being consumed. // since txids are unique, this will not be replayable. pick a tx output // that has not yet been consumed by someone else. hashStr := "" // put the input txid here // it'll work // also note that in bitcoin, all the 32-byte strings are displayed backwards. // who knows why. outpointTxid, err := chainhash.NewHashFromStr(hashStr) if err != nil { panic(err) } // let's try to spend output index 7 outPoint := wire.NewOutPoint( outpointTxid, 0) // replace 0 with the output you want to spend // create the TxIn, with empty sigscript field input := wire.NewTxIn(outPoint, nil, nil) // Next, we create the output. Outputs are [amount, address] pairs, where // amounts are 64-bit signed integers, and addresses are scripts that run on the // bitcoin stack interpreter // Put your wallet address as a string here, and it will be decoded into a 20-byte // hash. That hash is used in the standard "pay to pubkey hash" (p2pkh) script sendToAddressString := "" // put the address you're sending to here sendToAddress, err := btcutil.DecodeAddress(sendToAddressString, testnet3Parameters) if err != nil { panic(err) } // this builds an output script. sendToScript, err := txscript.PayToAddrScript(sendToAddress) // amounts in bitcoin are integers, but "one bitcoin" is actually 100 million of the // base unit, often called "satoshis". If the output amount is greater than the // input amount, the transaction is invalid (because it's creating more coins) // ( this check is performed over the sum of the inputs and outputs. There is an // exception for the coinbase transaction.) output := wire.NewTxOut(0, sendToScript) // replace 0 with the output value // put the inputs and outputs into the transaction tx.AddTxIn(input) tx.AddTxOut(output) // the transaction now has the inputs and outputs, but is missing the signature. // We need a private key to sign with // Hash any phrase to make a private key phraseHash := chainhash.DoubleHashB([]byte("secret phrase here")) // make a new private key struct. Private key structs also have a pubkey in them priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), phraseHash) fmt.Printf("my pubkey: %x\n", priv.PubKey().SerializeCompressed()) // we also need the script from the previous transaction. This is redundant as it // is covered by the txid prevAddressString := "" // put the address you're sending "from" here prevAddress, err := btcutil.DecodeAddress(prevAddressString, testnet3Parameters) if err != nil { panic(err) } spendFromScript, err := txscript.PayToAddrScript(prevAddress) // SignatureScript takes a bunch of arguments. In this case: // tx: transaction itself // hcahce: the hash cache // 0: which input to sign // spendFromScript: the previous output script (redundant, covered by input txid) // txscript.SigHashAll: the signature hash type. usually "all", meaning the // signature covers all inputs and outputs in the transaction. // true: the previous script has a compressed public key hash. pubSig, err := txscript.SignatureScript( tx, 0, spendFromScript, txscript.SigHashAll, priv, true) if err != nil { panic(err) } // once the signature has been created, we put the signature onto the // "witness stack" of the input. tx.TxIn[0].SignatureScript = pubSig return tx } ================================================ FILE: pset03/main.go ================================================ package main import ( "fmt" "github.com/btcsuite/btcd/chaincfg" ) var ( // we're running on testnet3 testnet3Parameters = &chaincfg.TestNet3Params ) func main() { fmt.Printf("mas.s62 pset03 - utxohunt\n") // Task #1 make an address pair // Call AddressFrom PrivateKey() to make a keypair // Task #2 make a transaction // Call EZTxBuilder to make a transaction // task 3, call OpReturnTxBuilder() the same way EZTxBuilder() was used return } ================================================ FILE: pset03/opreturn.go ================================================ package main import ( "github.com/btcsuite/btcd/btcec" "github.com/btcsuite/btcd/chaincfg/chainhash" "github.com/btcsuite/btcd/txscript" "github.com/btcsuite/btcd/wire" "github.com/btcsuite/btcutil" ) func OpReturnTxBuilder() *wire.MsgTx { // this function is similar to EZTxBuilder in that it builds a transaction. // however, it has 2 outputs. One is just sending money back to the same address, // and the other is an "op_return" output, which cannot be spent, but can // include arbitrary data that will be saved but ignored by the bitcoin network // create a new, empty transaction, set version to 2. Same as EZ tx := wire.NewMsgTx(2) // put the input txid here (your own) hashStr := "" outpointTxid, err := chainhash.NewHashFromStr(hashStr) if err != nil { panic(err) } // spend output index 0, which is the only output for that tx outPoint := wire.NewOutPoint(outpointTxid, 0) // create the TxIn, with empty sigscript field input := wire.NewTxIn(outPoint, nil, nil) // done with inputs for now. Build outputs. There will be 2 outputs, // the OP_RETURN output and the normal pubkey hash output. // The OP_RETURN output will be unspendable, so we should put 0 coins there. // Put a message here with your name or MIT ID number so I can find your // submission on the blockchain. opReturnData := []byte("your message here") // build the op_return output script // this is the OP_RETURN opcode, followed by a data push opcode, then the data. opReturnScript, err := txscript.NewScriptBuilder().AddOp(txscript.OP_RETURN).AddData(opReturnData).Script() if err != nil { panic(err) } // build the op_return output. opReturnOutput := wire.NewTxOut(0, opReturnScript) // keep it as 0 value // next, build the pubkey hash output. This the same as before in the EZ function. // put the address you're sending to here. It's the same as the address you're // spending from! sendToAddressString := "" sendToAddress, err := btcutil.DecodeAddress(sendToAddressString, testnet3Parameters) if err != nil { panic(err) } sendToScript, err := txscript.PayToAddrScript(sendToAddress) if err != nil { panic(err) } // put a bit less than your input amount, so that there is a fee for the miners // this will ensure miners put your transaction in a block. p2pkhOutput := wire.NewTxOut(123450000, sendToScript) // put the tx together, 1 input, 2 outputs. tx.AddTxIn(input) tx.AddTxOut(opReturnOutput) tx.AddTxOut(p2pkhOutput) // finally we need to sign. Same as EZ func. // we already know the address you're sending from spendFromScript, err := txscript.PayToAddrScript(sendToAddress) phraseHash := chainhash.DoubleHashB([]byte("private key here")) priv, _ := btcec.PrivKeyFromBytes(btcec.S256(), phraseHash) pubSig, err := txscript.SignatureScript( tx, 0, spendFromScript, txscript.SigHashAll, priv, true) if err != nil { panic(err) } tx.TxIn[0].SignatureScript = pubSig return tx }