├── .gitignore ├── Ring Signatures.pdf ├── README.md ├── block.go ├── LICENSE ├── varint.go ├── keccak.go ├── internal_test.go ├── address.go ├── LICENSE-MIT ├── keccak_test.go ├── varint_test.go ├── key.go ├── LICENSE-BSD ├── base58.go ├── address_test.go ├── ringsignature.go ├── transaction.go ├── ringct.go ├── edwards25519_test.go └── ringct_test.go /.gitignore: -------------------------------------------------------------------------------- 1 | *~ -------------------------------------------------------------------------------- /Ring Signatures.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paxosglobal/moneroutil/HEAD/Ring Signatures.pdf -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Monero Utilities 2 | ================ 3 | 4 | These are a set of utilities for working with Monero. 5 | 6 | Bitdevs Presentation 7 | ==================== 8 | 9 | Presentation and appendices can be found in "Ring Signatures.pdf" -------------------------------------------------------------------------------- /block.go: -------------------------------------------------------------------------------- 1 | package moneroutil 2 | 3 | type BlockHeader struct { 4 | majorVersion uint8 5 | minorVersion uint8 6 | timeStamp uint64 7 | previousHash Hash 8 | nonce uint32 9 | } 10 | 11 | type Block struct { 12 | BlockHeader 13 | MinerTx Transaction 14 | TxHashes []Hash 15 | } 16 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Monero Utilities 2 | Copyright (c) 2013 Go Authors 3 | Copyright (c) 2017 Paxos 4 | 5 | Portion of the code is Go Author's property, licensed under the BSD-3 license 6 | Portion of the code is Paxos's property, licensed under the MIT license 7 | 8 | const.go and edwards25519.go are licensed under BSD-3 (see LICENSE-BSD), all other files are licensed under MIT (see LICENSE-MIT). 9 | -------------------------------------------------------------------------------- /varint.go: -------------------------------------------------------------------------------- 1 | package moneroutil 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | func ReadVarInt(buf io.Reader) (result uint64, err error) { 9 | b := make([]byte, 1) 10 | var r uint64 11 | var n int 12 | for i := 0; ; i++ { 13 | n, err = buf.Read(b) 14 | if err != nil { 15 | return 16 | } 17 | if n != 1 { 18 | err = fmt.Errorf("Buffer ended prematurely for varint") 19 | return 20 | } 21 | r += (uint64(b[0]) & 0x7f) << uint(i*7) 22 | if uint64(b[0])&0x80 == 0 { 23 | break 24 | } 25 | } 26 | result = r 27 | return 28 | } 29 | 30 | func Uint64ToBytes(num uint64) (result []byte) { 31 | for ; num >= 0x80; num >>= 7 { 32 | result = append(result, byte((num&0x7f)|0x80)) 33 | } 34 | result = append(result, byte(num)) 35 | return 36 | } 37 | -------------------------------------------------------------------------------- /keccak.go: -------------------------------------------------------------------------------- 1 | package moneroutil 2 | 3 | import ( 4 | "github.com/ebfe/keccak" 5 | ) 6 | 7 | const ( 8 | ChecksumLength = 4 9 | HashLength = 32 10 | ) 11 | 12 | type Hash [HashLength]byte 13 | type Checksum [ChecksumLength]byte 14 | 15 | func Keccak256(data ...[]byte) (result Hash) { 16 | h := keccak.New256() 17 | for _, b := range data { 18 | h.Write(b) 19 | } 20 | r := h.Sum(nil) 21 | copy(result[:], r) 22 | return 23 | } 24 | 25 | func GetChecksum(data ...[]byte) (result Checksum) { 26 | keccak256 := Keccak256(data...) 27 | copy(result[:], keccak256[:4]) 28 | return 29 | } 30 | 31 | func Keccak512(data ...[]byte) (result Hash) { 32 | h := keccak.New512() 33 | for _, b := range data { 34 | h.Write(b) 35 | } 36 | r := h.Sum(nil) 37 | copy(result[:], r) 38 | return 39 | } 40 | -------------------------------------------------------------------------------- /internal_test.go: -------------------------------------------------------------------------------- 1 | package moneroutil 2 | 3 | import ( 4 | "encoding/hex" 5 | ) 6 | 7 | func HexToKey(h string) (result Key) { 8 | byteSlice, _ := hex.DecodeString(h) 9 | copy(result[:], byteSlice) 10 | return 11 | } 12 | 13 | func HexToHash(h string) (result Hash) { 14 | byteSlice, _ := hex.DecodeString(h) 15 | copy(result[:], byteSlice) 16 | return 17 | } 18 | 19 | // RandomPubKey takes a random scalar, interprets it as a point on the curve 20 | // and then multiplies by 8 to make it a point in the Group 21 | func RandomPubKey() (result *Key) { 22 | result = new(Key) 23 | p3 := new(ExtendedGroupElement) 24 | var p1 ProjectiveGroupElement 25 | var p2 CompletedGroupElement 26 | h := RandomScalar() 27 | p1.FromBytes(h) 28 | GeMul8(&p2, &p1) 29 | p2.ToExtended(p3) 30 | p3.ToBytes(result) 31 | return 32 | } 33 | -------------------------------------------------------------------------------- /address.go: -------------------------------------------------------------------------------- 1 | package moneroutil 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | type Address struct { 8 | network int 9 | spendingKey []byte 10 | viewingKey []byte 11 | } 12 | 13 | func (a *Address) Base58() (result string) { 14 | prefix := []byte{byte(a.network)} 15 | checksum := GetChecksum(prefix, a.spendingKey, a.viewingKey) 16 | result = EncodeMoneroBase58(prefix, a.spendingKey, a.viewingKey, checksum[:]) 17 | return 18 | } 19 | 20 | func NewAddress(address string) (result *Address, err string) { 21 | raw := DecodeMoneroBase58(address) 22 | if len(raw) != 69 { 23 | err = "Address is the wrong length" 24 | return 25 | } 26 | checksum := GetChecksum(raw[:65]) 27 | if bytes.Compare(checksum[:], raw[65:]) != 0 { 28 | err = "Checksum does not validate" 29 | return 30 | } 31 | result = &Address{ 32 | network: int(raw[0]), 33 | spendingKey: raw[1:33], 34 | viewingKey: raw[33:65], 35 | } 36 | return 37 | } 38 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Monero Utilities 2 | Copyright (c) 2017, Paxos 3 | 4 | Permission is hereby granted, free of charge, to any 5 | person obtaining a copy of this software and associated 6 | documentation files (the "Software"), to deal in the 7 | Software without restriction, including without 8 | limitation the rights to use, copy, modify, merge, 9 | publish, distribute, sublicense, and/or sell copies of 10 | the Software, and to permit persons to whom the Software 11 | is furnished to do so, subject to the following 12 | conditions: 13 | 14 | The above copyright notice and this permission notice 15 | shall be included in all copies or substantial portions 16 | of the Software. 17 | 18 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY 19 | KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE 20 | WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR 21 | PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 22 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 23 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT 24 | OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH 25 | THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /keccak_test.go: -------------------------------------------------------------------------------- 1 | package moneroutil 2 | 3 | import ( 4 | "encoding/hex" 5 | "testing" 6 | ) 7 | 8 | func TestKeccak256(t *testing.T) { 9 | tests := []struct { 10 | name string 11 | messageHex string 12 | wantHex string 13 | }{ 14 | { 15 | name: "from monero 1", 16 | messageHex: "c8fedd380dbae40ffb52", 17 | wantHex: "8e41962058b7422e7404253121489a3e63d186ed115086919a75105661483ba9", 18 | }, 19 | { 20 | name: "from monero 2", 21 | messageHex: "5020c4d530b6ec6cb4d9", 22 | wantHex: "8a597f11961935e32e0adeab2ce48b3df2d907c9b26619dad22f42ff65ab7593", 23 | }, 24 | { 25 | name: "from monero cryptotest.pl", 26 | messageHex: "0f3fe9c20b24a11bf4d6d1acd335c6a80543f1f0380590d7323caf1390c78e88", 27 | wantHex: "73b7a236f2a97c4e1805f7a319f1283e3276598567757186c526caf9a49e0a92", 28 | }, 29 | { 30 | name: "hello", 31 | messageHex: "68656c6c6f", 32 | wantHex: "1c8aff950685c2ed4bc3174f3472287b56d9517b9c948127319a09a7a36deac8", 33 | }, 34 | } 35 | for _, test := range tests { 36 | message, _ := hex.DecodeString(test.messageHex) 37 | got := Keccak256(message) 38 | want := HexToHash(test.wantHex) 39 | if want != got { 40 | t.Errorf("want %x, got %x", want, got) 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /varint_test.go: -------------------------------------------------------------------------------- 1 | package moneroutil 2 | 3 | import ( 4 | "bytes" 5 | "testing" 6 | ) 7 | 8 | func TestVarInt(t *testing.T) { 9 | tests := []struct { 10 | name string 11 | varInt []byte 12 | want uint64 13 | }{ 14 | { 15 | name: "1 byte", 16 | varInt: []byte{0x01}, 17 | want: 1, 18 | }, 19 | { 20 | name: "3 bytes", 21 | varInt: []byte{0x8f, 0xd6, 0x17}, 22 | want: 387855, 23 | }, 24 | { 25 | name: "4 bytes", 26 | varInt: []byte{0x80, 0x92, 0xf4, 0x01}, 27 | want: 4000000, 28 | }, 29 | { 30 | name: "7 bytes", 31 | varInt: []byte{0x80, 0xc0, 0xca, 0xf3, 0x84, 0xa3, 0x02}, 32 | want: 10000000000000, 33 | }, 34 | } 35 | var got uint64 36 | var err error 37 | var gotVarInt []byte 38 | buf := new(bytes.Buffer) 39 | for _, test := range tests { 40 | gotVarInt = Uint64ToBytes(test.want) 41 | if bytes.Compare(gotVarInt, test.varInt) != 0 { 42 | t.Errorf("%s: varint want %x, got %x", test.name, test.varInt, gotVarInt) 43 | continue 44 | } 45 | buf.Reset() 46 | buf.Write(test.varInt) 47 | got, err = ReadVarInt(buf) 48 | if err != nil { 49 | t.Errorf("%s: %s", test.name, err) 50 | continue 51 | } 52 | if test.want != got { 53 | t.Errorf("%s: want %d, got %d", test.name, test.want, got) 54 | continue 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /key.go: -------------------------------------------------------------------------------- 1 | package moneroutil 2 | 3 | import ( 4 | "crypto/rand" 5 | "io" 6 | ) 7 | 8 | const ( 9 | KeyLength = 32 10 | ) 11 | 12 | // Key can be a Scalar or a Point 13 | type Key [KeyLength]byte 14 | 15 | func (p *Key) FromBytes(b [KeyLength]byte) { 16 | *p = b 17 | } 18 | 19 | func (p *Key) ToBytes() (result [KeyLength]byte) { 20 | result = [KeyLength]byte(*p) 21 | return 22 | } 23 | 24 | func (p *Key) PubKey() (pubKey *Key) { 25 | point := new(ExtendedGroupElement) 26 | GeScalarMultBase(point, p) 27 | pubKey = new(Key) 28 | point.ToBytes(pubKey) 29 | return 30 | } 31 | 32 | // Creates a point on the Edwards Curve by hashing the key 33 | func (p *Key) HashToEC() (result *ExtendedGroupElement) { 34 | result = new(ExtendedGroupElement) 35 | var p1 ProjectiveGroupElement 36 | var p2 CompletedGroupElement 37 | h := Key(Keccak256(p[:])) 38 | p1.FromBytes(&h) 39 | GeMul8(&p2, &p1) 40 | p2.ToExtended(result) 41 | return 42 | } 43 | 44 | func RandomScalar() (result *Key) { 45 | result = new(Key) 46 | var reduceFrom [KeyLength * 2]byte 47 | tmp := make([]byte, KeyLength*2) 48 | rand.Read(tmp) 49 | copy(reduceFrom[:], tmp) 50 | ScReduce(result, &reduceFrom) 51 | return 52 | } 53 | 54 | func NewKeyPair() (privKey *Key, pubKey *Key) { 55 | privKey = RandomScalar() 56 | pubKey = privKey.PubKey() 57 | return 58 | } 59 | 60 | func ParseKey(buf io.Reader) (result Key, err error) { 61 | key := make([]byte, KeyLength) 62 | if _, err = buf.Read(key); err != nil { 63 | return 64 | } 65 | copy(result[:], key) 66 | return 67 | } 68 | -------------------------------------------------------------------------------- /LICENSE-BSD: -------------------------------------------------------------------------------- 1 | Copyright (c) 2009 The Go Authors. All rights reserved 2 | 3 | Redistribution and use in source and binary forms, with or without 4 | modification, are permitted provided that the following conditions are 5 | met: 6 | 7 | * Redistributions of source code must retain the above copyright 8 | notice, this list of conditions and the following disclaimer. 9 | * Redistributions in binary form must reproduce the above 10 | copyright notice, this list of conditions and the following disclaimer 11 | in the documentation and/or other materials provided with the 12 | distribution. 13 | * Neither the name of Google Inc. nor the names of its 14 | contributors may be used to endorse or promote products derived from 15 | this software without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 18 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 19 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 20 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 21 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 22 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 23 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 24 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 25 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 27 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /base58.go: -------------------------------------------------------------------------------- 1 | package moneroutil 2 | 3 | import ( 4 | "math/big" 5 | "strings" 6 | ) 7 | 8 | const BASE58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz" 9 | 10 | var base58Lookup = map[string]int{ 11 | "1": 0, "2": 1, "3": 2, "4": 3, "5": 4, "6": 5, "7": 6, "8": 7, 12 | "9": 8, "A": 9, "B": 10, "C": 11, "D": 12, "E": 13, "F": 14, "G": 15, 13 | "H": 16, "J": 17, "K": 18, "L": 19, "M": 20, "N": 21, "P": 22, "Q": 23, 14 | "R": 24, "S": 25, "T": 26, "U": 27, "V": 28, "W": 29, "X": 30, "Y": 31, 15 | "Z": 32, "a": 33, "b": 34, "c": 35, "d": 36, "e": 37, "f": 38, "g": 39, 16 | "h": 40, "i": 41, "j": 42, "k": 43, "m": 44, "n": 45, "o": 46, "p": 47, 17 | "q": 48, "r": 49, "s": 50, "t": 51, "u": 52, "v": 53, "w": 54, "x": 55, 18 | "y": 56, "z": 57, 19 | } 20 | var bigBase = big.NewInt(58) 21 | 22 | func encodeChunk(raw []byte, padding int) (result string) { 23 | remainder := new(big.Int) 24 | remainder.SetBytes(raw) 25 | bigZero := new(big.Int) 26 | for remainder.Cmp(bigZero) > 0 { 27 | current := new(big.Int) 28 | remainder.DivMod(remainder, bigBase, current) 29 | result = string(BASE58[current.Int64()]) + result 30 | } 31 | if len(result) < padding { 32 | result = strings.Repeat("1", (padding-len(result))) + result 33 | } 34 | return 35 | } 36 | 37 | func decodeChunk(encoded string) (result []byte) { 38 | bigResult := big.NewInt(0) 39 | currentMultiplier := big.NewInt(1) 40 | tmp := new(big.Int) 41 | for i := len(encoded) - 1; i >= 0; i-- { 42 | tmp.SetInt64(int64(base58Lookup[string(encoded[i])])) 43 | tmp.Mul(currentMultiplier, tmp) 44 | bigResult.Add(bigResult, tmp) 45 | currentMultiplier.Mul(currentMultiplier, bigBase) 46 | } 47 | result = bigResult.Bytes() 48 | return 49 | } 50 | 51 | func EncodeMoneroBase58(data ...[]byte) (result string) { 52 | var combined []byte 53 | for _, item := range data { 54 | combined = append(combined, item...) 55 | } 56 | length := len(combined) 57 | rounds := length / 8 58 | for i := 0; i < rounds; i++ { 59 | result += encodeChunk(combined[i*8:(i+1)*8], 11) 60 | } 61 | if length%8 > 0 { 62 | result += encodeChunk(combined[rounds*8:], 7) 63 | } 64 | return 65 | } 66 | 67 | func DecodeMoneroBase58(data string) (result []byte) { 68 | length := len(data) 69 | rounds := length / 11 70 | for i := 0; i < rounds; i++ { 71 | result = append(result, decodeChunk(data[i*11:(i+1)*11])...) 72 | } 73 | if length%11 > 0 { 74 | result = append(result, decodeChunk(data[rounds*11:])...) 75 | } 76 | return 77 | } 78 | -------------------------------------------------------------------------------- /address_test.go: -------------------------------------------------------------------------------- 1 | package moneroutil 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "testing" 7 | ) 8 | 9 | func TestAddressError(t *testing.T) { 10 | _, err := NewAddress("") 11 | want := "Address is the wrong length" 12 | if err != want { 13 | t.Errorf("want: %s, got: %s", want, err) 14 | } 15 | _, err = NewAddress("46w3n5EGhBeZkYmKvQRsd8UK9GhvcbYWQDobJape3NLMMFEjFZnJ3CnRmeKspubQGiP8iMTwFEX2QiBsjUkjKT4SSPd3fK1") 16 | want = "Checksum does not validate" 17 | if err != want { 18 | t.Errorf("want: %s, got: %s", want, err) 19 | } 20 | } 21 | 22 | func TestAddress(t *testing.T) { 23 | tests := []struct { 24 | name string 25 | network int 26 | spendingKeyHex string 27 | viewingKeyHex string 28 | address string 29 | }{ 30 | { 31 | name: "generic", 32 | network: MainNetwork, 33 | spendingKeyHex: "8c1a9d5ff5aaf1c3cdeb2a1be62f07a34ae6b15fe47a254c8bc240f348271679", 34 | viewingKeyHex: "0a29b163e392eb9416a52907fd7d3b84530f8d02ff70b1f63e72fdcb54cf7fe1", 35 | address: "46w3n5EGhBeZkYmKvQRsd8UK9GhvcbYWQDobJape3NLMMFEjFZnJ3CnRmeKspubQGiP8iMTwFEX2QiBsjUkjKT4SSPd3fKp", 36 | }, 37 | { 38 | name: "generic 2", 39 | network: MainNetwork, 40 | spendingKeyHex: "5007b84275af9a173c2080683afce90b2157ab640c18ddd5ce3e060a18a9ce99", 41 | viewingKeyHex: "27024b45150037b677418fcf11ba9675494ffdf994f329b9f7a8f8402b7934a0", 42 | address: "44f1Y84r9Lu4tQdLWRxV122rygfhUeVBrcmBaqcYCwUHScmf1ht8DFLXX9YN4T7nPPLcpqYLUdrFiY77nQYeH9RuK9gg4p6", 43 | }, 44 | { 45 | name: "require 1 padding in middle", 46 | network: MainNetwork, 47 | spendingKeyHex: "6add197bd82866e8bfbf1dc2fdf49873ec5f679059652da549cd806f2b166756", 48 | viewingKeyHex: "f5cf2897088fda0f7ac1c42491ed7d558a46ee41d0c81d038fd53ff4360afda0", 49 | address: "45fzHekTd5FfvxWBPYX2TqLPbtWjaofxYUeWCi6BRQXYFYd85sY2qw73bAuKhqY7deFJr6pN3STY81bZ9x2Zf4nGKASksqe", 50 | }, 51 | { 52 | name: "require 1 padding in last chunk", 53 | network: MainNetwork, 54 | spendingKeyHex: "50defe92d88b19aaf6bf66f061dd4380b79866a4122b25a03bceb571767dbe7b", 55 | viewingKeyHex: "f8f6f28283921bf5a17f0bcf4306233fc25ce9b6276154ad0de22aebc5c67702", 56 | address: "44grjkXtDHJVbZgtU1UKnrNXidcHfZ3HWToU5WjR3KgHMjgwrYLjXC6i5vm3HCp4vnBfYaNEyNiuZVwqtHD2SenS1JBRyco", 57 | }, 58 | { 59 | name: "testnet", 60 | network: TestNetwork, 61 | spendingKeyHex: "8de9cce254e60cd940abf6c77ef344c3a21fad74320e45734fbfcd5870e5c875", 62 | viewingKeyHex: "27024b45150037b677418fcf11ba9675494ffdf994f329b9f7a8f8402b7934a0", 63 | address: "9xYZvCDf6aFdLd7Qawg5XHZitWLKoeFvcLHfe5GxsGCFLbXSWeQNKciXX9YN4T7nPPLcpqYLUdrFiY77nQYeH9RuK9bogZJ", 64 | }, 65 | } 66 | var base58 string 67 | var spendingKey, viewingKey []byte 68 | for _, test := range tests { 69 | spendingKey, _ = hex.DecodeString(test.spendingKeyHex) 70 | viewingKey, _ = hex.DecodeString(test.viewingKeyHex) 71 | address, _ := NewAddress(test.address) 72 | if address.network != test.network { 73 | t.Errorf("%s: want: %d, got: %d", test.name, test.network, address.network) 74 | continue 75 | } 76 | if bytes.Compare(address.spendingKey, spendingKey) != 0 { 77 | t.Errorf("%s: want: %x, got: %x", test.name, spendingKey, address.spendingKey) 78 | continue 79 | } 80 | if bytes.Compare(address.viewingKey, viewingKey) != 0 { 81 | t.Errorf("%s: want: %x, got: %x", test.name, viewingKey, address.viewingKey) 82 | continue 83 | } 84 | base58 = address.Base58() 85 | if base58 != test.address { 86 | t.Errorf("%s: want: %s, got: %s", test.name, test.address, base58) 87 | continue 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /ringsignature.go: -------------------------------------------------------------------------------- 1 | package moneroutil 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "math/rand" 7 | ) 8 | 9 | type RingSignatureElement struct { 10 | c *Key 11 | r *Key 12 | } 13 | 14 | type RingSignature []*RingSignatureElement 15 | 16 | func (r *RingSignatureElement) Serialize() (result []byte) { 17 | result = make([]byte, 2*KeyLength) 18 | copy(result[:KeyLength], r.c[:]) 19 | copy(result[KeyLength:2*KeyLength], r.r[:]) 20 | return 21 | } 22 | 23 | func (r *RingSignature) Serialize() (result []byte) { 24 | result = make([]byte, len(*r)*KeyLength*2) 25 | for i := 0; i < len(*r); i++ { 26 | copy(result[i*KeyLength*2:(i+1)*KeyLength*2], (*r)[i].Serialize()) 27 | } 28 | return 29 | } 30 | 31 | func NewRingSignatureElement() (r *RingSignatureElement) { 32 | r = &RingSignatureElement{ 33 | c: new(Key), 34 | r: new(Key), 35 | } 36 | return 37 | } 38 | 39 | func ParseSignature(buf io.Reader) (result *RingSignatureElement, err error) { 40 | rse := NewRingSignatureElement() 41 | c := make([]byte, KeyLength) 42 | n, err := buf.Read(c) 43 | if err != nil { 44 | return 45 | } 46 | if n != KeyLength { 47 | err = fmt.Errorf("Not enough bytes for signature c") 48 | return 49 | } 50 | copy(rse.c[:], c) 51 | r := make([]byte, KeyLength) 52 | n, err = buf.Read(r) 53 | if err != nil { 54 | return 55 | } 56 | if n != KeyLength { 57 | err = fmt.Errorf("Not enough bytes for signature r") 58 | return 59 | } 60 | copy(rse.r[:], r) 61 | result = rse 62 | return 63 | } 64 | 65 | func ParseSignatures(mixinLengths []int, buf io.Reader) (signatures []RingSignature, err error) { 66 | // mixinLengths is the number of mixins at each input position 67 | sigs := make([]RingSignature, len(mixinLengths), len(mixinLengths)) 68 | for i, nMixin := range mixinLengths { 69 | sigs[i] = make([]*RingSignatureElement, nMixin, nMixin) 70 | for j := 0; j < nMixin; j++ { 71 | sigs[i][j], err = ParseSignature(buf) 72 | if err != nil { 73 | return 74 | } 75 | } 76 | } 77 | signatures = sigs 78 | return 79 | } 80 | 81 | func HashToScalar(data ...[]byte) (result *Key) { 82 | result = new(Key) 83 | *result = Key(Keccak256(data...)) 84 | ScReduce32(result) 85 | return 86 | } 87 | 88 | func CreateSignature(prefixHash *Hash, mixins []Key, privKey *Key) (keyImage Key, pubKeys []Key, sig RingSignature) { 89 | point := privKey.PubKey().HashToEC() 90 | keyImagePoint := new(ProjectiveGroupElement) 91 | GeScalarMult(keyImagePoint, privKey, point) 92 | // convert key Image point from Projective to Extended 93 | // in order to precompute 94 | keyImagePoint.ToBytes(&keyImage) 95 | keyImageGe := new(ExtendedGroupElement) 96 | keyImageGe.FromBytes(&keyImage) 97 | var keyImagePre [8]CachedGroupElement 98 | GePrecompute(&keyImagePre, keyImageGe) 99 | k := RandomScalar() 100 | pubKeys = make([]Key, len(mixins)+1) 101 | privIndex := rand.Intn(len(pubKeys)) 102 | pubKeys[privIndex] = *privKey.PubKey() 103 | r := make([]*RingSignatureElement, len(pubKeys)) 104 | sum := new(Key) 105 | toHash := prefixHash[:] 106 | for i := 0; i < len(pubKeys); i++ { 107 | tmpE := new(ExtendedGroupElement) 108 | tmpP := new(ProjectiveGroupElement) 109 | var tmpEBytes, tmpPBytes Key 110 | if i == privIndex { 111 | GeScalarMultBase(tmpE, k) 112 | tmpE.ToBytes(&tmpEBytes) 113 | toHash = append(toHash, tmpEBytes[:]...) 114 | tmpE = privKey.PubKey().HashToEC() 115 | GeScalarMult(tmpP, k, tmpE) 116 | tmpP.ToBytes(&tmpPBytes) 117 | toHash = append(toHash, tmpPBytes[:]...) 118 | } else { 119 | if i > privIndex { 120 | pubKeys[i] = mixins[i-1] 121 | } else { 122 | pubKeys[i] = mixins[i] 123 | } 124 | r[i] = &RingSignatureElement{ 125 | c: RandomScalar(), 126 | r: RandomScalar(), 127 | } 128 | tmpE.FromBytes(&pubKeys[i]) 129 | GeDoubleScalarMultVartime(tmpP, r[i].c, tmpE, r[i].r) 130 | tmpP.ToBytes(&tmpPBytes) 131 | toHash = append(toHash, tmpPBytes[:]...) 132 | tmpE = pubKeys[i].HashToEC() 133 | GeDoubleScalarMultPrecompVartime(tmpP, r[i].r, tmpE, r[i].c, &keyImagePre) 134 | tmpP.ToBytes(&tmpPBytes) 135 | toHash = append(toHash, tmpPBytes[:]...) 136 | ScAdd(sum, sum, r[i].c) 137 | } 138 | } 139 | h := HashToScalar(toHash) 140 | r[privIndex] = NewRingSignatureElement() 141 | ScSub(r[privIndex].c, h, sum) 142 | ScMulSub(r[privIndex].r, r[privIndex].c, privKey, k) 143 | sig = r 144 | return 145 | } 146 | 147 | func VerifySignature(prefixHash *Hash, keyImage *Key, pubKeys []Key, ringSignature RingSignature) (result bool) { 148 | keyImageGe := new(ExtendedGroupElement) 149 | if !keyImageGe.FromBytes(keyImage) { 150 | result = false 151 | return 152 | } 153 | var keyImagePre [8]CachedGroupElement 154 | GePrecompute(&keyImagePre, keyImageGe) 155 | toHash := prefixHash[:] 156 | tmpS, sum := new(Key), new(Key) 157 | for i, pubKey := range pubKeys { 158 | rse := ringSignature[i] 159 | if !ScValid(rse.c) || !ScValid(rse.r) { 160 | result = false 161 | return 162 | } 163 | tmpE := new(ExtendedGroupElement) 164 | tmpP := new(ProjectiveGroupElement) 165 | if !tmpE.FromBytes(&pubKey) { 166 | result = false 167 | return 168 | } 169 | var tmpPBytes, tmpEBytes Key 170 | GeDoubleScalarMultVartime(tmpP, rse.c, tmpE, rse.r) 171 | tmpP.ToBytes(&tmpPBytes) 172 | toHash = append(toHash, tmpPBytes[:]...) 173 | tmpE = pubKey.HashToEC() 174 | tmpE.ToBytes(&tmpEBytes) 175 | GeDoubleScalarMultPrecompVartime(tmpP, rse.r, tmpE, rse.c, &keyImagePre) 176 | tmpP.ToBytes(&tmpPBytes) 177 | toHash = append(toHash, tmpPBytes[:]...) 178 | ScAdd(sum, sum, rse.c) 179 | } 180 | tmpS = HashToScalar(toHash) 181 | ScSub(sum, tmpS, sum) 182 | result = ScIsZero(sum) 183 | return 184 | } 185 | -------------------------------------------------------------------------------- /transaction.go: -------------------------------------------------------------------------------- 1 | package moneroutil 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | const ( 9 | txInGenMarker = 0xff 10 | txInToKeyMarker = 2 11 | txOutToKeyMarker = 2 12 | ) 13 | 14 | var UnimplementedError = fmt.Errorf("Unimplemented") 15 | 16 | type txInGen struct { 17 | height uint64 18 | } 19 | 20 | type txInToKey struct { 21 | amount uint64 22 | keyOffsets []uint64 23 | keyImage Key 24 | } 25 | 26 | type TxInSerializer interface { 27 | TxInSerialize() []byte 28 | MixinLen() int 29 | } 30 | 31 | type TxOut struct { 32 | amount uint64 33 | key Key 34 | } 35 | 36 | type TransactionPrefix struct { 37 | version uint32 38 | unlockTime uint64 39 | vin []TxInSerializer 40 | vout []*TxOut 41 | extra []byte 42 | } 43 | 44 | type Transaction struct { 45 | TransactionPrefix 46 | signatures []RingSignature 47 | rctSignature *RctSig 48 | expanded bool 49 | } 50 | 51 | func (h *Hash) Serialize() (result []byte) { 52 | result = h[:] 53 | return 54 | } 55 | 56 | func (p *Key) Serialize() (result []byte) { 57 | result = p[:] 58 | return 59 | } 60 | 61 | func (t *TxOut) Serialize() (result []byte) { 62 | result = append(Uint64ToBytes(t.amount), txOutToKeyMarker) 63 | result = append(result, t.key[:]...) 64 | return 65 | } 66 | 67 | func (t *TxOut) String() (result string) { 68 | result = fmt.Sprintf("key: %x", t.key) 69 | return 70 | } 71 | 72 | func (t *txInGen) TxInSerialize() (result []byte) { 73 | result = append([]byte{txInGenMarker}, Uint64ToBytes(t.height)...) 74 | return 75 | } 76 | 77 | func (t *txInGen) MixinLen() int { 78 | return 0 79 | } 80 | 81 | func (t *txInToKey) TxInSerialize() (result []byte) { 82 | result = append([]byte{txInToKeyMarker}, Uint64ToBytes(t.amount)...) 83 | result = append(result, Uint64ToBytes(uint64(len(t.keyOffsets)))...) 84 | for _, keyOffset := range t.keyOffsets { 85 | result = append(result, Uint64ToBytes(keyOffset)...) 86 | } 87 | result = append(result, t.keyImage[:]...) 88 | return 89 | } 90 | 91 | func (t *txInToKey) MixinLen() int { 92 | return len(t.keyOffsets) 93 | } 94 | 95 | func (t *TransactionPrefix) SerializePrefix() (result []byte) { 96 | result = append(Uint64ToBytes(uint64(t.version)), Uint64ToBytes(t.unlockTime)...) 97 | result = append(result, Uint64ToBytes(uint64(len(t.vin)))...) 98 | for _, txIn := range t.vin { 99 | result = append(result, txIn.TxInSerialize()...) 100 | } 101 | result = append(result, Uint64ToBytes(uint64(len(t.vout)))...) 102 | for _, txOut := range t.vout { 103 | result = append(result, txOut.Serialize()...) 104 | } 105 | result = append(result, Uint64ToBytes(uint64(len(t.extra)))...) 106 | result = append(result, t.extra...) 107 | return 108 | } 109 | 110 | func (t *TransactionPrefix) PrefixHash() (hash Hash) { 111 | hash = Keccak256(t.SerializePrefix()) 112 | return 113 | } 114 | 115 | func (t *TransactionPrefix) OutputSum() (sum uint64) { 116 | for _, output := range t.vout { 117 | sum += output.amount 118 | } 119 | return 120 | } 121 | 122 | func (t *Transaction) Serialize() (result []byte) { 123 | result = t.SerializePrefix() 124 | if t.version == 1 { 125 | for i := 0; i < len(t.signatures); i++ { 126 | result = append(result, t.signatures[i].Serialize()...) 127 | } 128 | } else { 129 | result = append(result, t.rctSignature.SerializeBase()...) 130 | result = append(result, t.rctSignature.SerializePrunable()...) 131 | } 132 | return 133 | } 134 | 135 | func (t *Transaction) SerializeBase() (result []byte) { 136 | if t.version == 1 { 137 | result = t.Serialize() 138 | } else { 139 | result = append(t.SerializePrefix(), t.rctSignature.SerializeBase()...) 140 | } 141 | return 142 | } 143 | 144 | // ExpandTransaction does nothing for version 1 transactions, but for version 2 145 | // derives all the implied elements of the ring signature 146 | func (t *Transaction) ExpandTransaction(outputKeys [][]CtKey) { 147 | if t.version == 1 { 148 | return 149 | } 150 | r := t.rctSignature 151 | if r.sigType == RCTTypeNull { 152 | return 153 | } 154 | 155 | // fill in the outPk property of the ring signature 156 | for i, ctKey := range r.outPk { 157 | ctKey.destination = t.vout[i].key 158 | } 159 | 160 | r.message = Key(t.PrefixHash()) 161 | if r.sigType == RCTTypeFull { 162 | r.mixRing = make([][]CtKey, len(outputKeys[0])) 163 | for i := 0; i < len(outputKeys); i++ { 164 | r.mixRing[i] = make([]CtKey, len(outputKeys)) 165 | for j := 0; j < len(outputKeys[0]); j++ { 166 | r.mixRing[j][i] = outputKeys[i][j] 167 | } 168 | } 169 | r.mlsagSigs = make([]MlsagSig, 1) 170 | r.mlsagSigs[0].ii = make([]Key, len(t.vin)) 171 | for i, txIn := range t.vin { 172 | txInWithKey, _ := txIn.(*txInToKey) 173 | r.mlsagSigs[0].ii[i] = txInWithKey.keyImage 174 | } 175 | } else if r.sigType == RCTTypeSimple { 176 | r.mixRing = outputKeys 177 | r.mlsagSigs = make([]MlsagSig, len(t.vin)) 178 | for i, txIn := range t.vin { 179 | txInWithKey, _ := txIn.(*txInToKey) 180 | r.mlsagSigs[i].ii = make([]Key, 1) 181 | r.mlsagSigs[i].ii[0] = txInWithKey.keyImage 182 | } 183 | } 184 | t.expanded = true 185 | } 186 | 187 | func (t *Transaction) GetHash() (result Hash) { 188 | if t.version == 1 { 189 | result = Keccak256(t.Serialize()) 190 | } else { 191 | // version 2 requires first computing 3 separate hashes 192 | // prefix, rctBase and rctPrunable 193 | // and then hashing the hashes together to get the final hash 194 | prefixHash := t.PrefixHash() 195 | rctBaseHash := t.rctSignature.BaseHash() 196 | rctPrunableHash := t.rctSignature.PrunableHash() 197 | result = Keccak256(prefixHash[:], rctBaseHash[:], rctPrunableHash[:]) 198 | } 199 | return 200 | } 201 | 202 | func ParseTxInGen(buf io.Reader) (txIn *txInGen, err error) { 203 | t := new(txInGen) 204 | t.height, err = ReadVarInt(buf) 205 | if err != nil { 206 | return 207 | } 208 | txIn = t 209 | return 210 | } 211 | 212 | func ParseTxInToKey(buf io.Reader) (txIn *txInToKey, err error) { 213 | t := new(txInToKey) 214 | t.amount, err = ReadVarInt(buf) 215 | if err != nil { 216 | return 217 | } 218 | keyOffsetLen, err := ReadVarInt(buf) 219 | if err != nil { 220 | return 221 | } 222 | t.keyOffsets = make([]uint64, keyOffsetLen, keyOffsetLen) 223 | for i := 0; i < int(keyOffsetLen); i++ { 224 | t.keyOffsets[i], err = ReadVarInt(buf) 225 | if err != nil { 226 | return 227 | } 228 | } 229 | pubKey := make([]byte, KeyLength) 230 | n, err := buf.Read(pubKey) 231 | if err != nil { 232 | return 233 | } 234 | if n != KeyLength { 235 | err = fmt.Errorf("Buffer not long enough for public key") 236 | return 237 | } 238 | copy(t.keyImage[:], pubKey) 239 | txIn = t 240 | return 241 | } 242 | 243 | func ParseTxIn(buf io.Reader) (txIn TxInSerializer, err error) { 244 | marker := make([]byte, 1) 245 | n, err := buf.Read(marker) 246 | if n != 1 { 247 | err = fmt.Errorf("Buffer not enough for TxIn") 248 | return 249 | } 250 | if err != nil { 251 | return 252 | } 253 | switch { 254 | case marker[0] == txInGenMarker: 255 | txIn, err = ParseTxInGen(buf) 256 | case marker[0] == txInToKeyMarker: 257 | txIn, err = ParseTxInToKey(buf) 258 | } 259 | return 260 | } 261 | 262 | func ParseTxOut(buf io.Reader) (txOut *TxOut, err error) { 263 | t := new(TxOut) 264 | t.amount, err = ReadVarInt(buf) 265 | if err != nil { 266 | return 267 | } 268 | marker := make([]byte, 1) 269 | n, err := buf.Read(marker) 270 | if err != nil { 271 | return 272 | } 273 | if n != 1 { 274 | err = fmt.Errorf("Buffer not long enough for TxOut") 275 | return 276 | } 277 | switch { 278 | case marker[0] == txOutToKeyMarker: 279 | t.key, err = ParseKey(buf) 280 | default: 281 | err = fmt.Errorf("Bad Marker") 282 | return 283 | } 284 | if err != nil { 285 | return 286 | } 287 | txOut = t 288 | return 289 | } 290 | 291 | func ParseExtra(buf io.Reader) (extra []byte, err error) { 292 | length, err := ReadVarInt(buf) 293 | if err != nil { 294 | return 295 | } 296 | e := make([]byte, int(length)) 297 | n, err := buf.Read(e) 298 | if err != nil { 299 | return 300 | } 301 | if n != int(length) { 302 | err = fmt.Errorf("Not enough bytes for extra") 303 | return 304 | } 305 | extra = e 306 | return 307 | } 308 | 309 | func ParseTransaction(buf io.Reader) (transaction *Transaction, err error) { 310 | t := new(Transaction) 311 | version, err := ReadVarInt(buf) 312 | if err != nil { 313 | return 314 | } 315 | t.version = uint32(version) 316 | t.unlockTime, err = ReadVarInt(buf) 317 | if err != nil { 318 | return 319 | } 320 | numInputs, err := ReadVarInt(buf) 321 | if err != nil { 322 | return 323 | } 324 | var mixinLengths []int 325 | t.vin = make([]TxInSerializer, int(numInputs), int(numInputs)) 326 | for i := 0; i < int(numInputs); i++ { 327 | t.vin[i], err = ParseTxIn(buf) 328 | if err != nil { 329 | return 330 | } 331 | mixinLen := t.vin[i].MixinLen() 332 | if mixinLen > 0 { 333 | mixinLengths = append(mixinLengths, mixinLen) 334 | } 335 | } 336 | numOutputs, err := ReadVarInt(buf) 337 | if err != nil { 338 | return 339 | } 340 | t.vout = make([]*TxOut, int(numOutputs), int(numOutputs)) 341 | for i := 0; i < int(numOutputs); i++ { 342 | t.vout[i], err = ParseTxOut(buf) 343 | if err != nil { 344 | return 345 | } 346 | } 347 | t.extra, err = ParseExtra(buf) 348 | if err != nil { 349 | return 350 | } 351 | if t.version == 1 { 352 | t.signatures, err = ParseSignatures(mixinLengths, buf) 353 | if err != nil { 354 | return 355 | } 356 | } else { 357 | var nMixins int 358 | if len(mixinLengths) > 0 { 359 | nMixins = mixinLengths[0] - 1 360 | } 361 | t.rctSignature, err = ParseRingCtSignature(buf, int(numInputs), int(numOutputs), nMixins) 362 | if err != nil { 363 | return 364 | } 365 | } 366 | transaction = t 367 | return 368 | } 369 | -------------------------------------------------------------------------------- /ringct.go: -------------------------------------------------------------------------------- 1 | package moneroutil 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | ) 7 | 8 | const ( 9 | RCTTypeNull = iota 10 | RCTTypeFull 11 | RCTTypeSimple 12 | ) 13 | 14 | // Pedersen Commitment is generated from this struct 15 | // C = aG + bH where a = mask and b = amount 16 | // senderPk is the one-time public key for ECDH exchange 17 | type ecdhTuple struct { 18 | mask Key 19 | amount Key 20 | senderPk Key 21 | } 22 | 23 | // Range proof commitments 24 | type Key64 [64]Key 25 | 26 | // Borromean Signature 27 | type BoroSig struct { 28 | s0 Key64 29 | s1 Key64 30 | ee Key 31 | } 32 | 33 | // MLSAG (Multilayered Linkable Spontaneous Anonymous Group) Signature 34 | type MlsagSig struct { 35 | ss [][]Key 36 | cc Key 37 | ii []Key 38 | } 39 | 40 | // Range Signature 41 | // Essentially data for a Borromean Signature 42 | type RangeSig struct { 43 | asig BoroSig 44 | ci Key64 45 | } 46 | 47 | // Confidential Transaction Keys 48 | type CtKey struct { 49 | destination Key 50 | mask Key 51 | } 52 | 53 | // Ring Confidential Signature parts that we have to keep 54 | type RctSigBase struct { 55 | sigType uint8 56 | message Key 57 | mixRing [][]CtKey 58 | pseudoOuts []Key 59 | ecdhInfo []ecdhTuple 60 | outPk []CtKey 61 | txFee uint64 62 | } 63 | 64 | // Ring Confidential Signature parts that we can just prune later 65 | type RctSigPrunable struct { 66 | rangeSigs []RangeSig 67 | mlsagSigs []MlsagSig 68 | } 69 | 70 | // Ring Confidential Signature struct that can verify everything 71 | type RctSig struct { 72 | RctSigBase 73 | RctSigPrunable 74 | } 75 | 76 | func (k *Key) ToExtended() (result *ExtendedGroupElement) { 77 | result = new(ExtendedGroupElement) 78 | result.FromBytes(k) 79 | return 80 | } 81 | 82 | func identity() (result *Key) { 83 | result = new(Key) 84 | result[0] = 1 85 | return 86 | } 87 | 88 | // convert a uint64 to a scalar 89 | func d2h(val uint64) (result *Key) { 90 | result = new(Key) 91 | for i := 0; val > 0; i++ { 92 | result[i] = byte(val & 0xFF) 93 | val /= 256 94 | } 95 | return 96 | } 97 | 98 | // multiply a scalar by H (second curve point of Pedersen Commitment) 99 | func ScalarMultH(scalar *Key) (result *Key) { 100 | h := new(ExtendedGroupElement) 101 | h.FromBytes(&H) 102 | resultPoint := new(ProjectiveGroupElement) 103 | GeScalarMult(resultPoint, scalar, h) 104 | result = new(Key) 105 | resultPoint.ToBytes(result) 106 | return 107 | } 108 | 109 | // add two points together 110 | func AddKeys(sum, k1, k2 *Key) { 111 | a := k1.ToExtended() 112 | b := new(CachedGroupElement) 113 | k2.ToExtended().ToCached(b) 114 | c := new(CompletedGroupElement) 115 | geAdd(c, a, b) 116 | tmp := new(ExtendedGroupElement) 117 | c.ToExtended(tmp) 118 | tmp.ToBytes(sum) 119 | return 120 | } 121 | 122 | // compute a*G + b*B 123 | func AddKeys2(result, a, b, B *Key) { 124 | BPoint := B.ToExtended() 125 | RPoint := new(ProjectiveGroupElement) 126 | GeDoubleScalarMultVartime(RPoint, b, BPoint, a) 127 | RPoint.ToBytes(result) 128 | return 129 | } 130 | 131 | // subtract two points A - B 132 | func SubKeys(diff, k1, k2 *Key) { 133 | a := k1.ToExtended() 134 | b := new(CachedGroupElement) 135 | k2.ToExtended().ToCached(b) 136 | c := new(CompletedGroupElement) 137 | geSub(c, a, b) 138 | tmp := new(ExtendedGroupElement) 139 | c.ToExtended(tmp) 140 | tmp.ToBytes(diff) 141 | return 142 | } 143 | 144 | func (k *Key64) Serialize() (result []byte) { 145 | for _, key := range k { 146 | result = append(result, key[:]...) 147 | } 148 | return 149 | } 150 | 151 | func (b *BoroSig) Serialize() (result []byte) { 152 | result = append(b.s0.Serialize(), b.s1.Serialize()...) 153 | result = append(result, b.ee[:]...) 154 | return 155 | } 156 | 157 | func (r *RangeSig) Serialize() (result []byte) { 158 | result = append(r.asig.Serialize(), r.ci.Serialize()...) 159 | return 160 | } 161 | 162 | func (m *MlsagSig) Serialize() (result []byte) { 163 | for i := 0; i < len(m.ss); i++ { 164 | for j := 0; j < len(m.ss[i]); j++ { 165 | result = append(result, m.ss[i][j][:]...) 166 | } 167 | } 168 | result = append(result, m.cc[:]...) 169 | return 170 | } 171 | 172 | func (r *RctSigBase) SerializeBase() (result []byte) { 173 | result = []byte{r.sigType} 174 | // Null type returns right away 175 | if r.sigType == RCTTypeNull { 176 | return 177 | } 178 | result = append(result, Uint64ToBytes(r.txFee)...) 179 | if r.sigType == RCTTypeSimple { 180 | for _, input := range r.pseudoOuts { 181 | result = append(result, input[:]...) 182 | } 183 | } 184 | for _, ecdh := range r.ecdhInfo { 185 | result = append(result, ecdh.mask[:]...) 186 | result = append(result, ecdh.amount[:]...) 187 | } 188 | for _, ctKey := range r.outPk { 189 | result = append(result, ctKey.mask[:]...) 190 | } 191 | return 192 | } 193 | 194 | func (r *RctSigBase) BaseHash() (result Hash) { 195 | result = Keccak256(r.SerializeBase()) 196 | return 197 | } 198 | 199 | func (r *RctSig) SerializePrunable() (result []byte) { 200 | if r.sigType == RCTTypeNull { 201 | return 202 | } 203 | for _, rangeSig := range r.rangeSigs { 204 | result = append(result, rangeSig.Serialize()...) 205 | } 206 | for _, mlsagSig := range r.mlsagSigs { 207 | result = append(result, mlsagSig.Serialize()...) 208 | } 209 | return 210 | } 211 | 212 | func (r *RctSig) PrunableHash() (result Hash) { 213 | if r.sigType == RCTTypeNull { 214 | return 215 | } 216 | result = Keccak256(r.SerializePrunable()) 217 | return 218 | } 219 | 220 | func verBorromean(b *BoroSig, p1, p2 *Key64) bool { 221 | var data []byte 222 | tmp, tmp2 := new(Key), new(Key) 223 | for i := 0; i < 64; i++ { 224 | AddKeys2(tmp, &b.s0[i], &b.ee, &p1[i]) 225 | tmp3 := HashToScalar(tmp[:]) 226 | AddKeys2(tmp2, &b.s1[i], tmp3, &p2[i]) 227 | data = append(data, tmp2[:]...) 228 | } 229 | computed := HashToScalar(data) 230 | return *computed == b.ee 231 | } 232 | 233 | func verRange(c *Key, as RangeSig) bool { 234 | var CiH Key64 235 | tmp := identity() 236 | for i := 0; i < 64; i++ { 237 | SubKeys(&CiH[i], &as.ci[i], &H2[i]) 238 | AddKeys(tmp, tmp, &as.ci[i]) 239 | } 240 | if *c != *tmp { 241 | return false 242 | } 243 | return verBorromean(&as.asig, &as.ci, &CiH) 244 | } 245 | 246 | // Verify a RCTTypeSimple RingCT Signature 247 | func (r *RctSig) VerifyRctSimple() bool { 248 | sumOutPks := identity() 249 | for _, ctKey := range r.outPk { 250 | AddKeys(sumOutPks, sumOutPks, &ctKey.mask) 251 | } 252 | txFeeKey := ScalarMultH(d2h(r.txFee)) 253 | AddKeys(sumOutPks, sumOutPks, txFeeKey) 254 | sumPseudoOuts := identity() 255 | for _, pseudoOut := range r.pseudoOuts { 256 | AddKeys(sumPseudoOuts, sumPseudoOuts, &pseudoOut) 257 | } 258 | if *sumPseudoOuts != *sumOutPks { 259 | return false 260 | } 261 | for i, ctKey := range r.outPk { 262 | if !verRange(&ctKey.mask, r.rangeSigs[i]) { 263 | return false 264 | } 265 | } 266 | return true 267 | } 268 | 269 | func (r *RctSig) VerifyRctFull() bool { 270 | for i, ctKey := range r.outPk { 271 | if !verRange(&ctKey.mask, r.rangeSigs[i]) { 272 | return false 273 | } 274 | } 275 | return true 276 | } 277 | 278 | func ParseCtKey(buf io.Reader) (result CtKey, err error) { 279 | if result.mask, err = ParseKey(buf); err != nil { 280 | return 281 | } 282 | return 283 | } 284 | 285 | func ParseKey64(buf io.Reader) (result Key64, err error) { 286 | for i := 0; i < 64; i++ { 287 | if result[i], err = ParseKey(buf); err != nil { 288 | return 289 | } 290 | } 291 | return 292 | } 293 | 294 | func ParseBoroSig(buf io.Reader) (result BoroSig, err error) { 295 | if result.s0, err = ParseKey64(buf); err != nil { 296 | return 297 | } 298 | if result.s1, err = ParseKey64(buf); err != nil { 299 | return 300 | } 301 | if result.ee, err = ParseKey(buf); err != nil { 302 | return 303 | } 304 | return 305 | } 306 | 307 | func ParseRangeSig(buf io.Reader) (result RangeSig, err error) { 308 | if result.asig, err = ParseBoroSig(buf); err != nil { 309 | return 310 | } 311 | if result.ci, err = ParseKey64(buf); err != nil { 312 | return 313 | } 314 | return 315 | } 316 | 317 | func ParseRingCtSignature(buf io.Reader, nInputs, nOutputs, nMixin int) (result *RctSig, err error) { 318 | r := new(RctSig) 319 | sigType := make([]byte, 1) 320 | _, err = buf.Read(sigType) 321 | if err != nil { 322 | return 323 | } 324 | r.sigType = uint8(sigType[0]) 325 | if r.sigType == RCTTypeNull { 326 | result = r 327 | return 328 | } 329 | if r.sigType != RCTTypeFull || r.sigType != RCTTypeSimple { 330 | err = fmt.Errorf("Bad sigType %d", r.sigType) 331 | } 332 | r.txFee, err = ReadVarInt(buf) 333 | if err != nil { 334 | return 335 | } 336 | var nMg, nSS int 337 | if r.sigType == RCTTypeSimple { 338 | nMg = nInputs 339 | nSS = 2 340 | r.pseudoOuts = make([]Key, nInputs) 341 | for i := 0; i < nInputs; i++ { 342 | if r.pseudoOuts[i], err = ParseKey(buf); err != nil { 343 | return 344 | } 345 | } 346 | } else { 347 | nMg = 1 348 | nSS = nInputs + 1 349 | } 350 | r.ecdhInfo = make([]ecdhTuple, nOutputs) 351 | for i := 0; i < nOutputs; i++ { 352 | if r.ecdhInfo[i].mask, err = ParseKey(buf); err != nil { 353 | return 354 | } 355 | if r.ecdhInfo[i].amount, err = ParseKey(buf); err != nil { 356 | return 357 | } 358 | } 359 | r.outPk = make([]CtKey, nOutputs) 360 | for i := 0; i < nOutputs; i++ { 361 | if r.outPk[i], err = ParseCtKey(buf); err != nil { 362 | return 363 | } 364 | } 365 | r.rangeSigs = make([]RangeSig, nOutputs) 366 | for i := 0; i < nOutputs; i++ { 367 | if r.rangeSigs[i], err = ParseRangeSig(buf); err != nil { 368 | return 369 | } 370 | } 371 | r.mlsagSigs = make([]MlsagSig, nMg) 372 | for i := 0; i < nMg; i++ { 373 | r.mlsagSigs[i].ss = make([][]Key, nMixin+1) 374 | for j := 0; j < nMixin+1; j++ { 375 | r.mlsagSigs[i].ss[j] = make([]Key, nSS) 376 | for k := 0; k < nSS; k++ { 377 | if r.mlsagSigs[i].ss[j][k], err = ParseKey(buf); err != nil { 378 | return 379 | } 380 | } 381 | } 382 | if r.mlsagSigs[i].cc, err = ParseKey(buf); err != nil { 383 | return 384 | } 385 | } 386 | result = r 387 | return 388 | } 389 | -------------------------------------------------------------------------------- /edwards25519_test.go: -------------------------------------------------------------------------------- 1 | package moneroutil 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestScMulSub(t *testing.T) { 8 | tests := []struct { 9 | name string 10 | aHex string 11 | bHex string 12 | cHex string 13 | wantHex string 14 | }{ 15 | { 16 | name: "simple", 17 | aHex: "0100000000000000000000000000000000000000000000000000000000000000", 18 | bHex: "0100000000000000000000000000000000000000000000000000000000000000", 19 | cHex: "0200000000000000000000000000000000000000000000000000000000000000", 20 | wantHex: "0100000000000000000000000000000000000000000000000000000000000000", 21 | }, 22 | { 23 | name: "more complex", 24 | aHex: "1000000000000000000000000000000000000000000000000000000000000000", 25 | bHex: "1000000000000000000000000000000000000000000000000000000000000000", 26 | cHex: "0002000000000000000000000000000000000000000000000000000000000000", 27 | wantHex: "0001000000000000000000000000000000000000000000000000000000000000", 28 | }, 29 | { 30 | name: "more complex", 31 | aHex: "0000000000000000000000000000000000000000000000000000000000000010", 32 | bHex: "0000000000000000000000000000000000000000000000000000000000000010", 33 | cHex: "0000000000000000000000000000000000000000000000000000000000000000", 34 | wantHex: "844ae3b1946c2475b8f95e806867dbac410ae82d8c1331c265cf83e4be664c0e", 35 | }, 36 | } 37 | for _, test := range tests { 38 | a := HexToKey(test.aHex) 39 | b := HexToKey(test.bHex) 40 | c := HexToKey(test.cHex) 41 | want := HexToKey(test.wantHex) 42 | var got Key 43 | ScMulSub(&got, &a, &b, &c) 44 | if want != got { 45 | t.Errorf("%s: want %x, got %x", test.name, want, got) 46 | } 47 | } 48 | } 49 | 50 | func TestScalarMult(t *testing.T) { 51 | tests := []struct { 52 | name string 53 | scalarHex string 54 | pointHex string 55 | wantHex string 56 | }{ 57 | { 58 | name: "zero", 59 | scalarHex: "0000000000000000000000000000000000000000000000000000000000000000", 60 | pointHex: "0100000000000000000000000000000000000000000000000000000000000000", 61 | wantHex: "0100000000000000000000000000000000000000000000000000000000000000", 62 | }, 63 | { 64 | name: "basepoint * 1", 65 | scalarHex: "0100000000000000000000000000000000000000000000000000000000000000", 66 | pointHex: "5866666666666666666666666666666666666666666666666666666666666666", 67 | wantHex: "5866666666666666666666666666666666666666666666666666666666666666", 68 | }, 69 | { 70 | name: "basepoint * 8", 71 | scalarHex: "0800000000000000000000000000000000000000000000000000000000000000", 72 | pointHex: "5866666666666666666666666666666666666666666666666666666666666666", 73 | wantHex: "b4b937fca95b2f1e93e41e62fc3c78818ff38a66096fad6e7973e5c90006d321", 74 | }, 75 | { 76 | name: "basepoint * 2", 77 | scalarHex: "0200000000000000000000000000000000000000000000000000000000000000", 78 | pointHex: "2f1132ca61ab38dff00f2fea3228f24c6c71d58085b80e47e19515cb27e8d047", 79 | wantHex: "b4b937fca95b2f1e93e41e62fc3c78818ff38a66096fad6e7973e5c90006d321", 80 | }, 81 | } 82 | for _, test := range tests { 83 | scalarBytes := HexToKey(test.scalarHex) 84 | pointBytes := HexToKey(test.pointHex) 85 | want := HexToKey(test.wantHex) 86 | point := new(ExtendedGroupElement) 87 | point.FromBytes(&pointBytes) 88 | result := new(ProjectiveGroupElement) 89 | GeScalarMult(result, &scalarBytes, point) 90 | var got Key 91 | result.ToBytes(&got) 92 | if want != got { 93 | t.Errorf("%s: want %x, got %x", test.name, want, got) 94 | } 95 | } 96 | } 97 | 98 | func TestGeMul8(t *testing.T) { 99 | tests := []struct { 100 | name string 101 | pointHex string 102 | wantHex string 103 | }{ 104 | { 105 | name: "zero", 106 | pointHex: "0100000000000000000000000000000000000000000000000000000000000000", 107 | wantHex: "0100000000000000000000000000000000000000000000000000000000000000", 108 | }, 109 | { 110 | name: "basepoint", 111 | pointHex: "5866666666666666666666666666666666666666666666666666666666666666", 112 | wantHex: "b4b937fca95b2f1e93e41e62fc3c78818ff38a66096fad6e7973e5c90006d321", 113 | }, 114 | } 115 | for _, test := range tests { 116 | pointBytes := HexToKey(test.pointHex) 117 | want := HexToKey(test.wantHex) 118 | tmp := new(ExtendedGroupElement) 119 | tmp.FromBytes(&pointBytes) 120 | point := new(ProjectiveGroupElement) 121 | tmp.ToProjective(point) 122 | tmp2 := new(CompletedGroupElement) 123 | result := new(ExtendedGroupElement) 124 | var got Key 125 | GeMul8(tmp2, point) 126 | tmp2.ToExtended(result) 127 | result.ToBytes(&got) 128 | if want != got { 129 | t.Errorf("%s: want %x, got %x", test.name, want, got) 130 | } 131 | } 132 | } 133 | 134 | func TestGeDoubleScalarMultVartime(t *testing.T) { 135 | tests := []struct { 136 | name string 137 | pointHex string 138 | scalar1Hex string 139 | scalar2Hex string 140 | wantHex string 141 | }{ 142 | { 143 | name: "zero", 144 | pointHex: "0100000000000000000000000000000000000000000000000000000000000000", 145 | scalar1Hex: "0000000000000000000000000000000000000000000000000000000000000000", 146 | scalar2Hex: "0000000000000000000000000000000000000000000000000000000000000000", 147 | wantHex: "0100000000000000000000000000000000000000000000000000000000000000", 148 | }, 149 | { 150 | name: "8 times base point only", 151 | pointHex: "0100000000000000000000000000000000000000000000000000000000000000", 152 | scalar1Hex: "0000000000000000000000000000000000000000000000000000000000000000", 153 | scalar2Hex: "0800000000000000000000000000000000000000000000000000000000000000", 154 | wantHex: "b4b937fca95b2f1e93e41e62fc3c78818ff38a66096fad6e7973e5c90006d321", 155 | }, 156 | { 157 | name: "2 times non-base-point", 158 | pointHex: "2f1132ca61ab38dff00f2fea3228f24c6c71d58085b80e47e19515cb27e8d047", 159 | scalar1Hex: "0200000000000000000000000000000000000000000000000000000000000000", 160 | scalar2Hex: "0000000000000000000000000000000000000000000000000000000000000000", 161 | wantHex: "b4b937fca95b2f1e93e41e62fc3c78818ff38a66096fad6e7973e5c90006d321", 162 | }, 163 | { 164 | name: "Combination", 165 | pointHex: "2f1132ca61ab38dff00f2fea3228f24c6c71d58085b80e47e19515cb27e8d047", 166 | scalar1Hex: "0100000000000000000000000000000000000000000000000000000000000000", 167 | scalar2Hex: "0400000000000000000000000000000000000000000000000000000000000000", 168 | wantHex: "b4b937fca95b2f1e93e41e62fc3c78818ff38a66096fad6e7973e5c90006d321", 169 | }, 170 | } 171 | for _, test := range tests { 172 | pointBytes := HexToKey(test.pointHex) 173 | a := HexToKey(test.scalar1Hex) 174 | b := HexToKey(test.scalar2Hex) 175 | want := HexToKey(test.wantHex) 176 | point := new(ExtendedGroupElement) 177 | point.FromBytes(&pointBytes) 178 | result := new(ProjectiveGroupElement) 179 | GeDoubleScalarMultVartime(result, &a, point, &b) 180 | var got Key 181 | result.ToBytes(&got) 182 | if want != got { 183 | t.Errorf("%s: want %x, got %x", test.name, want, got) 184 | } 185 | } 186 | } 187 | 188 | func TestGeDoubleScalarMultPrecompVartime(t *testing.T) { 189 | tests := []struct { 190 | name string 191 | point1Hex string 192 | point2Hex string 193 | scalar1Hex string 194 | scalar2Hex string 195 | wantHex string 196 | }{ 197 | { 198 | name: "zero", 199 | point1Hex: "0100000000000000000000000000000000000000000000000000000000000000", 200 | point2Hex: "0100000000000000000000000000000000000000000000000000000000000000", 201 | scalar1Hex: "0000000000000000000000000000000000000000000000000000000000000000", 202 | scalar2Hex: "0000000000000000000000000000000000000000000000000000000000000000", 203 | wantHex: "0100000000000000000000000000000000000000000000000000000000000000", 204 | }, 205 | { 206 | name: "scalar 1 only", 207 | point1Hex: "5866666666666666666666666666666666666666666666666666666666666666", 208 | point2Hex: "0100000000000000000000000000000000000000000000000000000000000000", 209 | scalar1Hex: "0800000000000000000000000000000000000000000000000000000000000000", 210 | scalar2Hex: "0000000000000000000000000000000000000000000000000000000000000000", 211 | wantHex: "b4b937fca95b2f1e93e41e62fc3c78818ff38a66096fad6e7973e5c90006d321", 212 | }, 213 | { 214 | name: "scalar 2 only", 215 | point1Hex: "0100000000000000000000000000000000000000000000000000000000000000", 216 | point2Hex: "5866666666666666666666666666666666666666666666666666666666666666", 217 | scalar1Hex: "0000000000000000000000000000000000000000000000000000000000000000", 218 | scalar2Hex: "0800000000000000000000000000000000000000000000000000000000000000", 219 | wantHex: "b4b937fca95b2f1e93e41e62fc3c78818ff38a66096fad6e7973e5c90006d321", 220 | }, 221 | { 222 | name: "Combination", 223 | point1Hex: "2f1132ca61ab38dff00f2fea3228f24c6c71d58085b80e47e19515cb27e8d047", 224 | point2Hex: "5866666666666666666666666666666666666666666666666666666666666666", 225 | scalar1Hex: "0100000000000000000000000000000000000000000000000000000000000000", 226 | scalar2Hex: "0400000000000000000000000000000000000000000000000000000000000000", 227 | wantHex: "b4b937fca95b2f1e93e41e62fc3c78818ff38a66096fad6e7973e5c90006d321", 228 | }, 229 | } 230 | for _, test := range tests { 231 | point1Bytes := HexToKey(test.point1Hex) 232 | point2Bytes := HexToKey(test.point2Hex) 233 | a := HexToKey(test.scalar1Hex) 234 | b := HexToKey(test.scalar2Hex) 235 | want := HexToKey(test.wantHex) 236 | point1 := new(ExtendedGroupElement) 237 | point1.FromBytes(&point1Bytes) 238 | point2 := new(ExtendedGroupElement) 239 | point2.FromBytes(&point2Bytes) 240 | var point2Precomp [8]CachedGroupElement 241 | GePrecompute(&point2Precomp, point2) 242 | result := new(ProjectiveGroupElement) 243 | GeDoubleScalarMultPrecompVartime(result, &a, point1, &b, &point2Precomp) 244 | var got Key 245 | result.ToBytes(&got) 246 | if want != got { 247 | t.Errorf("%s: want %x, got %x", test.name, want, got) 248 | } 249 | } 250 | } 251 | 252 | func TestScValid(t *testing.T) { 253 | // All tests from github.com/monero-project/monero/tests/crypto/tests.txt 254 | tests := []struct { 255 | scalarHex string 256 | valid bool 257 | }{ 258 | { 259 | scalarHex: "ac10e070c8574ef374bdd1c5dbe9bacfd927f9ae0705cf08018ff865f6092d0f", 260 | valid: true, 261 | }, 262 | { 263 | scalarHex: "fa939388e8cb0ffc5c776cc517edc2a9457c11a89820a7bee91654ce2e2fb300", 264 | valid: true, 265 | }, 266 | { 267 | scalarHex: "6d728f89e567522dffe81cf641c2e483e2cee7051c1b3cd9cd38cba09d2dc900", 268 | valid: true, 269 | }, 270 | { 271 | scalarHex: "18fd66f7a0874de792f12a1b2add7d294100ea454537ae5794d0abc91dbf098a", 272 | valid: false, 273 | }, 274 | { 275 | scalarHex: "fdb4cee00230c448aeaa4790dd12e855eca6223d81dab6bfe3fe99ce5e702325", 276 | valid: false, 277 | }, 278 | { 279 | scalarHex: "f3ab5c4a64f9afbd48c75ad25700996a69504d31c75a6aa8beb24d178cfc32ba", 280 | valid: false, 281 | }, 282 | { 283 | scalarHex: "167e88e9298926ecd66d32a890148a9adcfb80a7ecc69396edc5f5ee8848ce91", 284 | valid: false, 285 | }, 286 | { 287 | scalarHex: "a9172ced17605ab2ba82caec382f1176fe6a1e0b92a9c95679978b3999d22605", 288 | valid: true, 289 | }, 290 | { 291 | scalarHex: "44f65ce00e64cf29e07bf3843e8e43e83b8b26d3dfcd83160d9a3fedda3fc908", 292 | valid: true, 293 | }, 294 | { 295 | scalarHex: "69951c807647f4dea33f8a40b0ddef999a9e8efd1b1b176f45fb92aae6287000", 296 | valid: true, 297 | }, 298 | { 299 | scalarHex: "dcb6f94312b4b815101af10775d189301d063816b0aa9b9ac028c0eb9b17dd0e", 300 | valid: true, 301 | }, 302 | { 303 | scalarHex: "a1a9574a90bed7f58bfd4c3295e181e152a4a39f9ad56520947e94fea40dfe2e", 304 | valid: false, 305 | }, 306 | { 307 | scalarHex: "757ebb27b14433f3ed696cd291825eba555af0863862216a98cf3080432bda01", 308 | valid: true, 309 | }, 310 | { 311 | scalarHex: "a7401d68c7956d41a05d8c6c1a3cc692cc269ad8a5837f93ac2c7654abaebf0e", 312 | valid: true, 313 | }, 314 | { 315 | scalarHex: "7c84ef4d9627529723f5e171a88bba91118192e6e19929204a6d23f5a541850b", 316 | valid: true, 317 | }, 318 | { 319 | scalarHex: "ebe4b2f9eadd52271e1115aa3b89b7bb3f68d58bf74d75486b77e6ae8216f609", 320 | valid: true, 321 | }, 322 | { 323 | scalarHex: "6221caf5dfd623587ca7aac45f5312faf1c2847d7bfbc5c46f88fd933970c866", 324 | valid: false, 325 | }, 326 | { 327 | scalarHex: "b38334b41f0b829bf3a2ea73b58c16afb56ccba5144668e78257ed4aa9a33a5e", 328 | valid: false, 329 | }, 330 | { 331 | scalarHex: "92e5df37f5412d9e9910cd28d27138ba46bdd4dee5dab1125f200f0a4e66f01c", 332 | valid: false, 333 | }, 334 | { 335 | scalarHex: "2d1717fe2965133009bdcf0c13b02ca85df4dc23d969d7b16fdab94341c2b90e", 336 | valid: true, 337 | }, 338 | { 339 | scalarHex: "82d771565024cf4b5c9add6d10a4114a2fbdbf7626f03749c8bddcf6c6feee03", 340 | valid: true, 341 | }, 342 | { 343 | scalarHex: "2db0f3d9bf47666bb40ae25ef7508296af2ef665a5333efff92c9daee5ecd986", 344 | valid: false, 345 | }, 346 | { 347 | scalarHex: "47883a3f0b0e624512887f3dae6f401b2f02eb8950eb8e741f07347108fbea14", 348 | valid: false, 349 | }, 350 | { 351 | scalarHex: "dae25f27f2756b8270f78015b1beafbd03a8d67a4dd940ee9550cecae9f3560f", 352 | valid: true, 353 | }, 354 | { 355 | scalarHex: "bab79d355572361259484ca77308943b7c71f5831be5dde11b3c7a26ac6e9a01", 356 | valid: true, 357 | }, 358 | { 359 | scalarHex: "f0bf30a9862d51fd8d1ca3a4e5a0e4330a5beab751af075b2e46f3e2d0297101", 360 | valid: true, 361 | }, 362 | { 363 | scalarHex: "a94270659333c985ab1461e9820f219416ad76f551a34f4c983ca967aa6b618c", 364 | valid: false, 365 | }, 366 | { 367 | scalarHex: "98e582e9d0e485e6b756e44b1cb187e9f2d4e16c02d2713a41dcee6d7fac0205", 368 | valid: true, 369 | }, 370 | { 371 | scalarHex: "62b57869238e72f05992e01f07c7a26c71ef9e3078ee7390cd4fc6406d3b37e3", 372 | valid: false, 373 | }, 374 | { 375 | scalarHex: "c751566f5420232cd626d2bd8073debedf219a0a28a7390200a2df9df8981647", 376 | valid: false, 377 | }, 378 | { 379 | scalarHex: "cfafede7d76def164bf15047a388365d712a600f1369bf692cb2dcb249f66508", 380 | valid: true, 381 | }, 382 | { 383 | scalarHex: "c0eb03c2c46a77b0cf67bea7d547b9b4d02116f70702790398bdabbfa775100a", 384 | valid: true, 385 | }, 386 | { 387 | scalarHex: "58c5afbb7b334e921bb46eeee1dfebadfbbc3a77c420b0b325b9470eff94cc0e", 388 | valid: true, 389 | }, 390 | { 391 | scalarHex: "b31b178b4dc8f7a7c24f6eb0987e32b8a9446ce8207186259074cc3d925c530d", 392 | valid: true, 393 | }, 394 | { 395 | scalarHex: "467b2c5f63120744b5aee7ec255d170aba83babdec9e76c2690300540eb17483", 396 | valid: false, 397 | }, 398 | { 399 | scalarHex: "9bc3be487bea81da020af0353660878e7deca693ac81278fac343b8b1809bd9c", 400 | valid: false, 401 | }, 402 | { 403 | scalarHex: "162cdb42f9d9147118537cc4d5eadb8f589f6de3929188f6c41428bdab2c650e", 404 | valid: true, 405 | }, 406 | { 407 | scalarHex: "6eeb88f685d1b5c99f8c27dcadf95f5bea9082e2735a4f8aafc3c3a35f749002", 408 | valid: true, 409 | }, 410 | { 411 | scalarHex: "e03bb5c9bedc41d324ca6d988e50b7e7c6cd6d2b1e471b752a00a10a19c9a604", 412 | valid: true, 413 | }, 414 | { 415 | scalarHex: "4fa064dcef2f14fc453c30865583468ad35af81c460a643b11285c15f2babb04", 416 | valid: true, 417 | }, 418 | { 419 | scalarHex: "bf067a7b53dab7fcd6a1f5a2c8b9b54c262e9be5ea5892dba5e4ad25585be702", 420 | valid: true, 421 | }, 422 | { 423 | scalarHex: "14d8d255c2156bb12133d5ee792061a21d3fbda2f14310753f3a90c2ad4ccbc8", 424 | valid: false, 425 | }, 426 | { 427 | scalarHex: "81e78a37b7e2dfed35ad8228a6c4a6f0aa34cc1de43da23895c5c86790804e01", 428 | valid: true, 429 | }, 430 | { 431 | scalarHex: "62366805970fb07b877bf1705d9c10517c6667ab7586d9afa82c30236a72f50f", 432 | valid: true, 433 | }, 434 | { 435 | scalarHex: "bb3b0e27b7c57052e5ac20bc6390229f57c6dbc8213dbc4ee1cbb9e174e389f9", 436 | valid: false, 437 | }, 438 | { 439 | scalarHex: "5c4039f162531ce9129667ed424446ef375a18da246a72c0dab00321fa04e929", 440 | valid: false, 441 | }, 442 | { 443 | scalarHex: "8d41a450d98a6f354266e36510921649e5229412976f8f318a3090d390a46807", 444 | valid: true, 445 | }, 446 | { 447 | scalarHex: "2fbce4441723379b8b6ef417803eb9220c8fee42f72da7e36e2c64b51628a10c", 448 | valid: true, 449 | }, 450 | { 451 | scalarHex: "5bbf0e5b58e3df89a5f4c607e3033fff8b9643fc900813f4ea50b889260bb004", 452 | valid: true, 453 | }, 454 | { 455 | scalarHex: "e5acfcb2ecb7175f90aa24db107cea1be7a45bc471c4c65b9f46535768545de5", 456 | valid: false, 457 | }, 458 | { 459 | scalarHex: "fae6e8d7226e6dc25faafdd8b5c63b68587980f6e76ea59b8595f18f64a96bd5", 460 | valid: false, 461 | }, 462 | { 463 | scalarHex: "6736f7939157ccee066a40056c13d96230ca5ffd014b0ebb0a454e3d8ea6830f", 464 | valid: true, 465 | }, 466 | { 467 | scalarHex: "b2b490d52f6e4d9ef14fc1ab3e1e97160693ba5d1220e4d63956eca316693504", 468 | valid: true, 469 | }, 470 | { 471 | scalarHex: "dca490cb4e21589e045150e8c0876871f08e6b3886c6393c6348bfb432799079", 472 | valid: false, 473 | }, 474 | { 475 | scalarHex: "ca2663d84aa7d7581ac4f0a270a685019178f5288013d163028d321e92b04901", 476 | valid: true, 477 | }, 478 | { 479 | scalarHex: "7dd277472249a53620d6b21c1ab0424024581b5ae3e6ecb9a46af8511b55a408", 480 | valid: true, 481 | }, 482 | { 483 | scalarHex: "30f07adb405de73987d81fcba3d523d420d130fe753afbf3dd19d5f08e8a1356", 484 | valid: false, 485 | }, 486 | { 487 | scalarHex: "cc31de9889440c695df43a9c621a7156bc064b161be2fdef35dd8ae0e1017fe2", 488 | valid: false, 489 | }, 490 | { 491 | scalarHex: "962ac1be6677cf0d298dd1244501e6a164a915df61b83c2837f80a84541e2e03", 492 | valid: true, 493 | }, 494 | { 495 | scalarHex: "e0a1e1c48f5dfb7c0faddd9186214ec2f81fb0bc3aca423ccf0e538cf43a2bd5", 496 | valid: false, 497 | }, 498 | { 499 | scalarHex: "8ed93780cbd46092acce856a8c691edbb8419deec5acfb26fcf82419b4a964b9", 500 | valid: false, 501 | }, 502 | { 503 | scalarHex: "f62cf00cac227d2b96e7f96268f0d606ad5924b0efb64e623e25584f993c93a8", 504 | valid: false, 505 | }, 506 | { 507 | scalarHex: "e49eacd6858efad9f3bedba8b9edff5ff3d5bd622926d5e96e6e8c8752a43d25", 508 | valid: false, 509 | }, 510 | { 511 | scalarHex: "ed424965b9a79c2f6611d7d4bcbe5bae4dbf2c8130141c714acc95c492061411", 512 | valid: false, 513 | }, 514 | { 515 | scalarHex: "7ea2bb2a4dab662c3adf71eb959314208ff9dbf50b3cb1a7940e4a4d6d797e0e", 516 | valid: true, 517 | }, 518 | { 519 | scalarHex: "96fc939742b8ed518bb7aa4bb6c9bbe3fddded6b9441d9ea27712c377e0bdff1", 520 | valid: false, 521 | }, 522 | { 523 | scalarHex: "c5122ad5575b54ebce3f1d1d97c3bb0cfeb9e5ea3bb223ab63189ced40579cf4", 524 | valid: false, 525 | }, 526 | { 527 | scalarHex: "c9147f1b42890b089ae2dfdc27f69b4a29c67b8bc0264259fcf71c39e31b893c", 528 | valid: false, 529 | }, 530 | { 531 | scalarHex: "9292516356f70e84852c3a563970bff369dfc39325c64432610baa0f31f2320e", 532 | valid: true, 533 | }, 534 | { 535 | scalarHex: "85f8b6251f177701b319c5db4bc372a5cf05cb07d86b2b2d5c938b0faf6d670f", 536 | valid: true, 537 | }, 538 | { 539 | scalarHex: "de88df59b4e8e3fe2beab575b4c6ffccbd24cf294f890fa61cbc231b0c976f0d", 540 | valid: true, 541 | }, 542 | { 543 | scalarHex: "2922bd9b50e09fe75a3745c62afd5bd1813dcffcf4503668e71d8f5d37c0090d", 544 | valid: true, 545 | }, 546 | { 547 | scalarHex: "f090b3ed9355221e4fb40397c367fb31a2c7ce308ba96b9c4fc9610c14cbe305", 548 | valid: true, 549 | }, 550 | { 551 | scalarHex: "1c545854bd134a995f2138d6d088032c33a9a23a30748615652e6675d56e670f", 552 | valid: true, 553 | }, 554 | { 555 | scalarHex: "588050d78a3079a12bc88d6bcaf34a95bc6928c9233f14056277bf36bb355d26", 556 | valid: false, 557 | }, 558 | { 559 | scalarHex: "3956481800462e5c34aa2178dde33d5096c23185d61a5839c212d11e61adc901", 560 | valid: true, 561 | }, 562 | { 563 | scalarHex: "d0d61e471d5d503a6efcd549694b61851a58b0c8f8fb400227e2a48c1cbe4a0d", 564 | valid: true, 565 | }, 566 | { 567 | scalarHex: "1eed64f825d3f1bba4a226425dbef91f102ec01bcf4a0ac0a00baef8de20c405", 568 | valid: true, 569 | }, 570 | { 571 | scalarHex: "86764f74c855d580581ff17416a2c394de55bb3cb6e676756e86f3893ee61a09", 572 | valid: true, 573 | }, 574 | { 575 | scalarHex: "a1cc5ecb2a528ab8fe3efac17a2859c2ef1b81b51d18643bb3ee3fcc8794eb80", 576 | valid: false, 577 | }, 578 | { 579 | scalarHex: "740464b60e54da6a3541057742d327330ae5ac190e2b86708f5af7a1ff89fd0a", 580 | valid: true, 581 | }, 582 | { 583 | scalarHex: "ada63ca80e15912177b738c7c7618917827893b57ad20688a0a9c93c6c7c4e0a", 584 | valid: true, 585 | }, 586 | { 587 | scalarHex: "a55a6f1f2cab41704b251d3c64f3123f1635e3ee24fcce7e88cdaaf148636500", 588 | valid: true, 589 | }, 590 | { 591 | scalarHex: "515bfea540021ffb6d8f2324952a1ffa02f40adb860cff143ff0d797bd63fb09", 592 | valid: true, 593 | }, 594 | { 595 | scalarHex: "2784fbff1cad8d63b47359691caf8c7972219d7f9a64ee25fdbfe970981b2e87", 596 | valid: false, 597 | }, 598 | { 599 | scalarHex: "596071a52ac2e1b55001170c7ae5ee8a66a52e4b14db270a66c4828eccfca8a5", 600 | valid: false, 601 | }, 602 | { 603 | scalarHex: "afee887ccf82dd3406988f4afafd0d779e43d1f0f1b5107b1c3aed15d4ab57ed", 604 | valid: false, 605 | }, 606 | { 607 | scalarHex: "fb38ec6e693ee2aed1364c29dc7a60a2a7cb10601582a0ca4f4a0ca2c0499e08", 608 | valid: true, 609 | }, 610 | { 611 | scalarHex: "7f890e7539b8b3e6510a3f523a764f582f9e2c791400678eee85bd5842978c06", 612 | valid: true, 613 | }, 614 | { 615 | scalarHex: "26efbf6e436e974860ee3b6c197afa55c29b115e68b488ba46c1e865fe37f894", 616 | valid: false, 617 | }, 618 | { 619 | scalarHex: "6f6d9f15f262dde42c60af6880e8c6b6f07cab7cad785c19866f9a5c37419240", 620 | valid: false, 621 | }, 622 | { 623 | scalarHex: "f90673b823b480de9e560948307e1ada8ff1f4380e74e89ee45aa7fecae1d308", 624 | valid: true, 625 | }, 626 | { 627 | scalarHex: "269abc577fd32b1625895a1117dbc367822f9849441d4dbf7f56ca633fbf50b8", 628 | valid: false, 629 | }, 630 | { 631 | scalarHex: "26fcbe1690ff0bc306a9ec3cef75bee1a5e39710d5c02d46334fb7d89e75dc0c", 632 | valid: true, 633 | }, 634 | { 635 | scalarHex: "48c827ac48cda0f2ccd29a37884a14fa9cebe2ddf76bcc4fdf827245835d1daa", 636 | valid: false, 637 | }, 638 | { 639 | scalarHex: "9c82c6403ec29e60ad812c1762610e00c05891c6eb0fbf2393b886f298397e1a", 640 | valid: false, 641 | }, 642 | { 643 | scalarHex: "260fb700bf36504bfc5bea76c115929746f4aa709ba831fddb64b59af9bfd704", 644 | valid: true, 645 | }, 646 | { 647 | scalarHex: "aaff292106a5dd831c507603f72fb7e0b9aa5ad990575a7a3dbe83744668c552", 648 | valid: false, 649 | }, 650 | { 651 | scalarHex: "aca3f81877ac78519ce2944063fd7c50530d0ba7de2fa20ebdb1107872b6f302", 652 | valid: true, 653 | }, 654 | { 655 | scalarHex: "1086efacf20320ba25eedd1cf8d3b28348b0bf3e2191b7585e16dc9e4f7f5803", 656 | valid: true, 657 | }, 658 | { 659 | scalarHex: "66905a80d3d06c38faa5212c7455e5f48cdd5f15d816025b6466c085ef6f2d51", 660 | valid: false, 661 | }, 662 | { 663 | scalarHex: "a9fb08088371cc78c2b514608cee6b43c534e657d4e1f7d0cfd9fd4173aca769", 664 | valid: false, 665 | }, 666 | { 667 | scalarHex: "590e0a35ac44640ecf0f1f89eba200c6d98245b87360496ef408297af0011673", 668 | valid: false, 669 | }, 670 | { 671 | scalarHex: "9a83a1ab418d8d9e92e0ecde0cb1246e4b25f407c5421a3997cda43ede83930b", 672 | valid: true, 673 | }, 674 | { 675 | scalarHex: "bd3b91b68594291e859ace170fedcc64adfff013ed9f066bc625326e452ebc54", 676 | valid: false, 677 | }, 678 | { 679 | scalarHex: "ef68e423644b94c8549a9a8ca49e396d642ef0fc28448b77b2bdee1487787a0f", 680 | valid: true, 681 | }, 682 | { 683 | scalarHex: "6a6fe13345ffd78567560c8dfb511f20edfc15067212aaee69bddb77d5b4860e", 684 | valid: true, 685 | }, 686 | { 687 | scalarHex: "a1fb1f4e3e88997e4e4017a8346d37671cad86cd5d795d2550b1b9c805dfdb0f", 688 | valid: true, 689 | }, 690 | { 691 | scalarHex: "99e61e19a4c6c94354a5b7a6e206663f677223a6d3fc78659d9eaec21ef38402", 692 | valid: true, 693 | }, 694 | { 695 | scalarHex: "d4bf37268f05041950d82420a78753d168b8b808628b48d6ce7b0ed330701d02", 696 | valid: true, 697 | }, 698 | { 699 | scalarHex: "fee112a3e2de6313bc8809aba4466720c552d3615538b0523163fb01cbd1258a", 700 | valid: false, 701 | }, 702 | { 703 | scalarHex: "b9692112ef467088d7f32a873719c7f2179be1445aef71eab73f218e6d6336b9", 704 | valid: false, 705 | }, 706 | { 707 | scalarHex: "9aa00cad08702566bc88d11a2e8a99b188efd0d5fb164a1b4f3fc7378b962800", 708 | valid: true, 709 | }, 710 | { 711 | scalarHex: "b4301f1630b288b9d6a8afed5c72c10c3c3ce8c935a7aed218e8ff3f70b4a539", 712 | valid: false, 713 | }, 714 | { 715 | scalarHex: "f7f4d3bd2a84cf95d8793bd5e792510d5e95b8ce833775572e901795b5e1bd01", 716 | valid: true, 717 | }, 718 | { 719 | scalarHex: "3b7d10a8b1a2695dc00c7f12fc12c0876b27509b1ffa452c8b80643f3cd26315", 720 | valid: false, 721 | }, 722 | { 723 | scalarHex: "be2fa0a79bfa9fad3d7e9be1e5961c4849a38a1f4243a0c55d65d019a4fc5c0b", 724 | valid: true, 725 | }, 726 | { 727 | scalarHex: "a03687639bb3d2e8506fcf8d062fbc342b499dfc26d36baea80edc87674d6b0f", 728 | valid: true, 729 | }, 730 | { 731 | scalarHex: "c995ca23c04d581d52f7e550a4531b837f185ef58362b13f237964c0663f0139", 732 | valid: false, 733 | }, 734 | { 735 | scalarHex: "32865bf3762338c5e13fba1ffcc08629498a31af695813f221ebfba72ff7714b", 736 | valid: false, 737 | }, 738 | { 739 | scalarHex: "a8f37b565fe66aa2e851a74904e98c3059f92a037979da3bd4a52a10f719bf09", 740 | valid: true, 741 | }, 742 | { 743 | scalarHex: "f7474c236309d9888f0d8a53ab5a22bb4d600f3687a4e1d2815b89f866523202", 744 | valid: true, 745 | }, 746 | { 747 | scalarHex: "090d4f638d250e4588d1a25888c0b9ac6c207e4a98a9ef5a69ad0b6b1ed7ce1f", 748 | valid: false, 749 | }, 750 | { 751 | scalarHex: "6b6daf404e2b6f4e2e0398baf8627fed108d0a0bc38a364e1b5d55bfd63f6d21", 752 | valid: false, 753 | }, 754 | { 755 | scalarHex: "7e83a7bbf587be0694738f36bf52b24577a3489d72f7426ec0886a37ece4f10d", 756 | valid: true, 757 | }, 758 | { 759 | scalarHex: "5f7abc23dcf726378db619bc2ce1e86ae209daa6d1909117ce7270829c4d3da3", 760 | valid: false, 761 | }, 762 | { 763 | scalarHex: "f9ffc537ad782ce9f00f80be30e8b822c901bf62b2433c365a2f856e65f88706", 764 | valid: true, 765 | }, 766 | { 767 | scalarHex: "9d3374f71e9ad3dbad360d8a80bc8b5c0d814eb09cade0c62bc094b858849004", 768 | valid: true, 769 | }, 770 | { 771 | scalarHex: "f97097fe5d46b78e030a5207f1db92df9c8efcb0051b6e3b65cb1a58dfab660d", 772 | valid: true, 773 | }, 774 | { 775 | scalarHex: "ead8314b9663a10344eaad550137ffb0906500057efd4a28b69def67bed8a10f", 776 | valid: true, 777 | }, 778 | { 779 | scalarHex: "38ca4c5eefa2a159c05eac5e05466db588c8fbbac7df6717e1c2fc1cd15e0956", 780 | valid: false, 781 | }, 782 | { 783 | scalarHex: "9b43159b9077fb2a049aa067e1d5c1a0d16da8515934b7588f4670483050ad0e", 784 | valid: true, 785 | }, 786 | { 787 | scalarHex: "a43c8bb23b53fcd288d71d301e9cdd225bdf88e5d58ef3b485d8318c19fb9b9d", 788 | valid: false, 789 | }, 790 | { 791 | scalarHex: "2278fffc9472031130714cd01599f4a476fe5b94cd092fdc5568917fb7a479f9", 792 | valid: false, 793 | }, 794 | { 795 | scalarHex: "b78718c39d1c90448ef8787350c8baaec08de36ecde9ac234eb570397761a703", 796 | valid: true, 797 | }, 798 | { 799 | scalarHex: "48a8506dd388eb99eab0d587bf4a7bd34e68f001eb88999a76f1878c32b8e478", 800 | valid: false, 801 | }, 802 | { 803 | scalarHex: "659eb800501cccb504ae9bdcf5779ba8b2d24e4f63824c84124c453a34dd3540", 804 | valid: false, 805 | }, 806 | { 807 | scalarHex: "1ce40f32aee47060a0dadf34ce04f81c48af3218051770fb86976785f981b05e", 808 | valid: false, 809 | }, 810 | { 811 | scalarHex: "e7360d88d6dbbf3c352b80811110235a213aa5a3ea182d36c8c670ba9610a00f", 812 | valid: true, 813 | }, 814 | { 815 | scalarHex: "82934fcaed4804020e1ce4ff798c30312aff862b1371f7f74d71b4316b413806", 816 | valid: true, 817 | }, 818 | { 819 | scalarHex: "5bb1383ae3547b8caeba767eaa73a8fc08ac5972f982c736568424f8665770ac", 820 | valid: false, 821 | }, 822 | { 823 | scalarHex: "533b7c9bde705efe0c08a8a0605ea2d4c3ec21919aeca99b40e10e29ca7dc255", 824 | valid: false, 825 | }, 826 | { 827 | scalarHex: "93c1411f47251750b1b95c481d0ce55b401722273932914e2d1e78924e08fb06", 828 | valid: true, 829 | }, 830 | { 831 | scalarHex: "5b90a4fd7d50881168802f7a8da19437305e38203b6bb6ac68b114b59dfbfa0e", 832 | valid: true, 833 | }, 834 | { 835 | scalarHex: "e98125b5ad8262054c811f92d0596863ef876cb86c7f6b5783aee2e8cd6a5300", 836 | valid: true, 837 | }, 838 | { 839 | scalarHex: "3b47eac89bc19bc46dde8fb091bb1128ddfb070cbac09b65d37416d62b1d9d3a", 840 | valid: false, 841 | }, 842 | { 843 | scalarHex: "4710f6af3f144eaa2a8f1f2af329bebe752d3594c73008bad237e5e0be0cf07f", 844 | valid: false, 845 | }, 846 | { 847 | scalarHex: "a6a654a6e2d2ecd7b1daea341897658b01ab34f7e84d9fc1b3ebf8214b39a840", 848 | valid: false, 849 | }, 850 | { 851 | scalarHex: "006ba25fa6dc45938b6ab7bf6fe7096bb0eee2fc17688995a3a01567739503d2", 852 | valid: false, 853 | }, 854 | { 855 | scalarHex: "ec2a2a700045b57c85c73770fbca5896aba8941ae39035a29641891ddb309a08", 856 | valid: true, 857 | }, 858 | { 859 | scalarHex: "a6ea18e936f99d7f284553e83545cf324784e5a263f47cdcd78cfc4da10cf80b", 860 | valid: true, 861 | }, 862 | { 863 | scalarHex: "86853a4044faaa87e2a949d9623d523eae1022467816a713488ef18de5db590a", 864 | valid: true, 865 | }, 866 | { 867 | scalarHex: "0f79400a90af899e64dc6c718cb2df4f8426cc77e1745e64af2dfc4743f4ef08", 868 | valid: true, 869 | }, 870 | { 871 | scalarHex: "4b9727146720b7b9fc2bb00b7b65b18909b53558fc5c4777e2efcb336ce9f103", 872 | valid: true, 873 | }, 874 | { 875 | scalarHex: "4a71e9840fb19d79255f06456bceda85029950a9d7aebd80f19da16672ea2e4f", 876 | valid: false, 877 | }, 878 | { 879 | scalarHex: "a76d81141e740c48642aeecb93f87edbe74c94d7795c35938979913f62355350", 880 | valid: false, 881 | }, 882 | { 883 | scalarHex: "fe82ea864b773ba785ae30742142fc95a6311f1caeacba19ed019a11b3ddee06", 884 | valid: true, 885 | }, 886 | { 887 | scalarHex: "c43a927dd10a6fc47bd3888aa9d9613ba258ad838832b923251a2863691c0600", 888 | valid: true, 889 | }, 890 | { 891 | scalarHex: "55d66194e087988015066ddc0237d061163797305034f8b54524e935f832ed08", 892 | valid: true, 893 | }, 894 | { 895 | scalarHex: "650c717392bdf31373dbeda48d99fd10b76080f0319b6039f48eedc8de8daa01", 896 | valid: true, 897 | }, 898 | { 899 | scalarHex: "ef8a542e46802078598d8f41489c0d618cc19ac897719f3b298ea22cca35030c", 900 | valid: true, 901 | }, 902 | { 903 | scalarHex: "6f8be2599f25b3645571a0d3edda8d354218f1bac4e22fed861fb472ff615a04", 904 | valid: true, 905 | }, 906 | { 907 | scalarHex: "78b99806f01c239ea3e717e15a509ffc2f3c7481b5eceb365f7eb8e2ab13d907", 908 | valid: true, 909 | }, 910 | { 911 | scalarHex: "2bae1ebc0fb83cade154f1daaed5d1f03581f350e733f2f0d56a73fa3c97cf2c", 912 | valid: false, 913 | }, 914 | { 915 | scalarHex: "4c9f5a53e544ae46926b1504af11a7500f57487e62836c3f37b445c2b701dc0e", 916 | valid: true, 917 | }, 918 | { 919 | scalarHex: "162003195972b318297a38632d9859bc86f1677154f94996970a320e0d23aa05", 920 | valid: true, 921 | }, 922 | { 923 | scalarHex: "9563b33d7a9a751d3c8ae2b4536bc2ed7cf5231051191653d7c08d502357ed0b", 924 | valid: true, 925 | }, 926 | { 927 | scalarHex: "2ffc2e41884b11129e1cc83aa95cd0b44dacb040a9ff1809a353597af8af0736", 928 | valid: false, 929 | }, 930 | { 931 | scalarHex: "ebfb376fb3a20871f35a54ecc2bf0003a2f2d00838196f0860e229c1db21e90c", 932 | valid: true, 933 | }, 934 | { 935 | scalarHex: "3da00eff81fd6771a23481fa8719fbe674c0dffa8db583e5fc0da7c9a0713006", 936 | valid: true, 937 | }, 938 | { 939 | scalarHex: "2d492ad9915f6274a6b62cdc245204527a1216b4ef0e7e1e1d3aa6d81f0c3905", 940 | valid: true, 941 | }, 942 | { 943 | scalarHex: "1a6d227bc48af4cff57ab11dc7ba3b7da21a9998b05dbeea4c05ca8bcffe2277", 944 | valid: false, 945 | }, 946 | { 947 | scalarHex: "8db67435b9e243dfbb58f252061ca3a005d197b941fa1f9cbbd29fbeafd507d4", 948 | valid: false, 949 | }, 950 | { 951 | scalarHex: "b2f744640c89049ebbbf94bd3013d3103ccf0e750a810543b39601116dc3300d", 952 | valid: true, 953 | }, 954 | { 955 | scalarHex: "8177e87433900f41dbdc22db054c79995f6f610647bcee7527f51091f5198274", 956 | valid: false, 957 | }, 958 | { 959 | scalarHex: "2c7cbaa6142473cecdb1c10bf9fc2430984a4b6cd34afc607668c6d88bf24f06", 960 | valid: true, 961 | }, 962 | { 963 | scalarHex: "a0a2ccb60bcbd0eda5dd11540e3517462bd841d58d4aed185beb5cfbda9e5f34", 964 | valid: false, 965 | }, 966 | { 967 | scalarHex: "767219f2581c07b1bbccd62fe4af10b4e202538fe918cedefbba3297bc89b604", 968 | valid: true, 969 | }, 970 | { 971 | scalarHex: "ed8828a708c013d80fe558be718b1e738063c4eca7528c06067b73d9dd2adccc", 972 | valid: false, 973 | }, 974 | { 975 | scalarHex: "4e81f8c4a0166a40db4063b31a5a4e511648b0f094d8930d956f74a7bec44f03", 976 | valid: true, 977 | }, 978 | { 979 | scalarHex: "09d5a71ca33bbc186f06b5ece548f25b0d008ad71c04e55d3d4c5fe0ef960b19", 980 | valid: false, 981 | }, 982 | { 983 | scalarHex: "51aa0d12359bc5819055618be5955824d81bd6e5ef52d51ee7e75aa9ca09edc4", 984 | valid: false, 985 | }, 986 | { 987 | scalarHex: "94927b83b0f167631a5c8eae13a9290c027f795dbd0c3d0f2d1a270b65db05e4", 988 | valid: false, 989 | }, 990 | { 991 | scalarHex: "e30c00666b11f332690267f3a2b8caa93a9a6e3961522a4ef886f3768f4c4166", 992 | valid: false, 993 | }, 994 | { 995 | scalarHex: "6ec7e22c9921501879a85c89c2833e04526538c6cbdecd8a040fe6c57c881c06", 996 | valid: true, 997 | }, 998 | { 999 | scalarHex: "54a4299c9d8572857247843d38842926fc88064905d8041974a1d9d407313a0f", 1000 | valid: true, 1001 | }, 1002 | { 1003 | scalarHex: "d4604b16400e77f37e8f6f5cc4ced1ef0da8f2a832bf6a0f51321d221111ce83", 1004 | valid: false, 1005 | }, 1006 | { 1007 | scalarHex: "a545d4cb99de9fc8f976356996351009628be30201dd42a11f7b379a5099c40b", 1008 | valid: true, 1009 | }, 1010 | { 1011 | scalarHex: "7db6f2c324cda1f3c9b39d659e5b5e39ad8e4b65e31e3afc10e91f890d6c0b9f", 1012 | valid: false, 1013 | }, 1014 | { 1015 | scalarHex: "afee4fe9a50c0d0aba669ca7f866ac4eb16e83afd843f142caf1132b9c5bcd11", 1016 | valid: false, 1017 | }, 1018 | { 1019 | scalarHex: "03cf1b6414acc2990bf4df95a9afa40aefcbc55946f8f40298cec7b45e942b09", 1020 | valid: true, 1021 | }, 1022 | { 1023 | scalarHex: "82c67f0cc06d1f5d878e710dc3734dc9b698fbd43b9cfaa50bc0734240631eee", 1024 | valid: false, 1025 | }, 1026 | { 1027 | scalarHex: "ef10fc4f79bdfade5af636fd41a7e5c48e966b5562693fd7a95568f52d472d06", 1028 | valid: true, 1029 | }, 1030 | { 1031 | scalarHex: "0ce4ab8a2f342f7ff2fd10b1b734d5333c7f8240075b2ae8522313d192d8a01d", 1032 | valid: false, 1033 | }, 1034 | { 1035 | scalarHex: "745a1228beb514bac4521c342b345f301fb28e7bfebb85d3c22497d4474fe904", 1036 | valid: true, 1037 | }, 1038 | { 1039 | scalarHex: "684cfecbb321a4236354e94fd8bc74c22a28f1a3ee7e21193b0cf264204fce51", 1040 | valid: false, 1041 | }, 1042 | { 1043 | scalarHex: "14a31b0cf4630e8e74730979d661c1902ab2f47823244ff7809be751b273b00c", 1044 | valid: true, 1045 | }, 1046 | { 1047 | scalarHex: "6c0c31a9e40faf4ead079582dae043bee80f27fbb8b76ffa5c960083d578ac0f", 1048 | valid: true, 1049 | }, 1050 | { 1051 | scalarHex: "ef49cd4030926ad3dbc08425fedfed7de48a1ca119259abec5600be5b60deb0f", 1052 | valid: true, 1053 | }, 1054 | { 1055 | scalarHex: "b432db4174848e42b9a680c74d66e746a48cb397b4008815064178cf7acec91c", 1056 | valid: false, 1057 | }, 1058 | { 1059 | scalarHex: "8c041643a91de5b9a652c3edb1b2512656daf33521197f7ab01056aa04d33708", 1060 | valid: true, 1061 | }, 1062 | { 1063 | scalarHex: "d683e2784298e070cd686eddc848e1ce3205c42822650cf9bc2301fb9a330564", 1064 | valid: false, 1065 | }, 1066 | { 1067 | scalarHex: "48ad5703dbf88a809d1bde1d295c4e51e3cf24215a3136c251c165505f80e84e", 1068 | valid: false, 1069 | }, 1070 | { 1071 | scalarHex: "19d22f694db4b90b5500af61b7d6a26d5568b4a3048ddef5709698ed6df8820b", 1072 | valid: true, 1073 | }, 1074 | { 1075 | scalarHex: "f8d5164c397f22c6b8cf3ae26ebfa4a9fe893afc2d7aec1f7c4f1e46c9eca306", 1076 | valid: true, 1077 | }, 1078 | { 1079 | scalarHex: "64a5a328e130e416cf1b3fb3accd3bd032f66df404f5364e0ec9d18d652196c2", 1080 | valid: false, 1081 | }, 1082 | { 1083 | scalarHex: "300931fb2c3670af36168883af39e10e1593945114e11c4eee9547d253f4f504", 1084 | valid: true, 1085 | }, 1086 | { 1087 | scalarHex: "0bbe9316029ea6d59883574d39d1fcd3af3344d73033e2b615f0883361a11602", 1088 | valid: true, 1089 | }, 1090 | { 1091 | scalarHex: "75609b2f7a8c04053ba7c1dbfc1b7c2a8f00720817d1abd2dc1db11645bbaaa1", 1092 | valid: false, 1093 | }, 1094 | { 1095 | scalarHex: "fa98e50dd3e3fa8308ffe861194920ec6b4f431eb34c67d7332a92830a7cb20d", 1096 | valid: true, 1097 | }, 1098 | { 1099 | scalarHex: "50b48576275008950b552a94e32d25c0835cb6dd44e146519848c9a7ee003707", 1100 | valid: true, 1101 | }, 1102 | { 1103 | scalarHex: "032487e8b30ddec54b5cf90cbc38dde71104169e48228c2336916f1575c1c005", 1104 | valid: true, 1105 | }, 1106 | { 1107 | scalarHex: "574fe8ee31b42bcb9224c34c34ddca2908db4b5f4f630ce76244d1257f81e504", 1108 | valid: true, 1109 | }, 1110 | { 1111 | scalarHex: "a4bba7f4765850dd77a67e56cf89e3c8967ba8f3c72ba14c7ea014202f81780b", 1112 | valid: true, 1113 | }, 1114 | { 1115 | scalarHex: "90eca818a69f6b8c52bddee273f37eaa96c58f2322fa5f09de36d606edc2ef04", 1116 | valid: true, 1117 | }, 1118 | { 1119 | scalarHex: "ac689dc28cb94239b78e6dd66249feec0519686e0a3f974ef8d4760d320de842", 1120 | valid: false, 1121 | }, 1122 | { 1123 | scalarHex: "a578412ffcd608f1fc4d77b954b5650d5016a24747e08664373a6c1c8d61c71a", 1124 | valid: false, 1125 | }, 1126 | { 1127 | scalarHex: "d5b2f9a4b3ae8dcfc1a2ade16b320d7d00180ad72e9ba500cfeceb4e45335c00", 1128 | valid: true, 1129 | }, 1130 | { 1131 | scalarHex: "ba26b0105b5d74df01e27ac128303cf4f139b2d164772ce56a31b5787b5d2207", 1132 | valid: true, 1133 | }, 1134 | { 1135 | scalarHex: "fb9e9e404f5901ffc02ba848ed33c98a88365ff161b4d484f67f8d4d2bc9fe85", 1136 | valid: false, 1137 | }, 1138 | { 1139 | scalarHex: "e326865f9ee2b68fdff8c36d39672f897669d9efb52bd4b4d037f4e219f72887", 1140 | valid: false, 1141 | }, 1142 | { 1143 | scalarHex: "8d911a2a666472a69614bbdd887169936e4e4384992cffac6f5c8ad3f81da708", 1144 | valid: true, 1145 | }, 1146 | { 1147 | scalarHex: "a9c04591640432f35dcdcdbe5027080c31613253f01a8e4fb1c2ed04f3e8c2a1", 1148 | valid: false, 1149 | }, 1150 | { 1151 | scalarHex: "e5e18376596aa363f1de26fb318eadaee85792bb7e51af164ba80da6e358ac03", 1152 | valid: true, 1153 | }, 1154 | { 1155 | scalarHex: "2632ef6ab45b46f2a48b5691c3e82e54e50ba74f23751aa602ec77a39596a10d", 1156 | valid: true, 1157 | }, 1158 | { 1159 | scalarHex: "f110202b14458789eb17321f757032e3515012143e8dd156a401d0b48ebb6287", 1160 | valid: false, 1161 | }, 1162 | { 1163 | scalarHex: "82d4274f7518f0cf92637657c69407a5fccb685ce9de8fa617a6f6f6b86c290d", 1164 | valid: true, 1165 | }, 1166 | { 1167 | scalarHex: "e1d9512e60b4dab6b55f14f56c320ad8eda6b8e378c5f873ce5d89c622c76c0c", 1168 | valid: true, 1169 | }, 1170 | { 1171 | scalarHex: "adf9a384148ff31f52f4e2365c5234b93edd574df1b5b664650859a764130f07", 1172 | valid: true, 1173 | }, 1174 | { 1175 | scalarHex: "f5c8043c4b5a3a1ded6d02af7e048d298872e8fedbdfdb544b881215313956f9", 1176 | valid: false, 1177 | }, 1178 | { 1179 | scalarHex: "cd7d1bd7c2bf26b7060d2407dbd88a5ba4add8044436bcbf6e00e690eae29699", 1180 | valid: false, 1181 | }, 1182 | { 1183 | scalarHex: "af5b7ede9808e45e80f427d27e660443851ee2063d53c23dacb487b6b375807f", 1184 | valid: false, 1185 | }, 1186 | { 1187 | scalarHex: "3a854a6125fbae08eeaf8f29177b83cec19f8f23f82e1c0213acfda03a19a057", 1188 | valid: false, 1189 | }, 1190 | { 1191 | scalarHex: "307e8bc876b71a1ad7da7e3f8687d3173a6605184b912d80b34cec7abd09fd05", 1192 | valid: true, 1193 | }, 1194 | { 1195 | scalarHex: "5936e762cfe201af05d2e7082a10e5f58cb71cee179406eb026b05437211f5a0", 1196 | valid: false, 1197 | }, 1198 | { 1199 | scalarHex: "c02d9bb9b3ce61da67d7eb839ad6135b28485be17b6749a5d07a3a442e33890a", 1200 | valid: true, 1201 | }, 1202 | { 1203 | scalarHex: "766459f999aa16992ec9639aed4e1ecdd7b78c6ab7cc63400f2f2dbad02796e7", 1204 | valid: false, 1205 | }, 1206 | { 1207 | scalarHex: "39be058fddfed6dee0840cd03f7c407c3a30b0a0e87cc8ada9772b4150fd9a07", 1208 | valid: true, 1209 | }, 1210 | { 1211 | scalarHex: "18c994dad23375e3860e21ce5d430ef2b876820bd0fe9e84a3b3a5ba45c790c5", 1212 | valid: false, 1213 | }, 1214 | { 1215 | scalarHex: "0583e64db042f0e22e7abd759ef1b51ab5155314caf362de34781e8da9b4a20b", 1216 | valid: true, 1217 | }, 1218 | { 1219 | scalarHex: "8d71c7122df856668387356a704a0bcd1865493b65ab254b24c9bd06c5414386", 1220 | valid: false, 1221 | }, 1222 | { 1223 | scalarHex: "ca94f5f35fe069a057e9a9431e7ea3b99c7efd9700c0f838d3b99ab82648d343", 1224 | valid: false, 1225 | }, 1226 | { 1227 | scalarHex: "850d3c5acb9103049e0ce06fcd64a543a38bf20fb31ce3a47e275d1e043c5506", 1228 | valid: true, 1229 | }, 1230 | { 1231 | scalarHex: "06580accc5c48aa72ce261547614b8629c10e2ee8d62971f8453b2007f91a60f", 1232 | valid: true, 1233 | }, 1234 | { 1235 | scalarHex: "bef7fb38fcb2d6a5c22f1db03c4c26a0f61eaaa1e7f846b2bb46ff06aa9ca407", 1236 | valid: true, 1237 | }, 1238 | { 1239 | scalarHex: "fc7956e14919bb2f531c6cba13e0bf3568db3927b24a2f9131e0c42aac0cbbb1", 1240 | valid: false, 1241 | }, 1242 | { 1243 | scalarHex: "bf2ed7b0fdd8cc1222db5ae9e699de5d54abee91a3f8bd988d248ab041738aa3", 1244 | valid: false, 1245 | }, 1246 | { 1247 | scalarHex: "126213a8e3de2ab1fc862ad8bb27d3b13de5ba55657bf6e04efcf4015dd70895", 1248 | valid: false, 1249 | }, 1250 | { 1251 | scalarHex: "d1729b969a9395788931713933c587ef3748c96976a488f6a1e2f1e1ed6973d0", 1252 | valid: false, 1253 | }, 1254 | { 1255 | scalarHex: "2448345de229bb4c2695f9653653301c4562ff4a60c568a3f483474b6800875c", 1256 | valid: false, 1257 | }, 1258 | { 1259 | scalarHex: "e72284802f2737c293838a6357251398101ec60ba6e8458304bbd25de570010b", 1260 | valid: true, 1261 | }, 1262 | { 1263 | scalarHex: "87c0898c4c283f24b7164fa8aaab9fc3b06ad5237b389d9735d58c54dd989186", 1264 | valid: false, 1265 | }, 1266 | { 1267 | scalarHex: "4cceb19c61263feaffeedc984280a6f5ecc1eeebeed05a407359661d61bdf8a6", 1268 | valid: false, 1269 | }, 1270 | { 1271 | scalarHex: "2e286296d5300eb4ae7200036acbd47a319d3f9a8eb66a986a36683b85b26002", 1272 | valid: true, 1273 | }, 1274 | { 1275 | scalarHex: "1b7140ff3a09a82554d4219425c2213abd16d9f342e30e2f7c05f2a5188432a4", 1276 | valid: false, 1277 | }, 1278 | { 1279 | scalarHex: "c0cdfe0ac8dd014bc1fb4c5d3f22d74f30623054f3008c0d1e34af4f2eafdec4", 1280 | valid: false, 1281 | }, 1282 | { 1283 | scalarHex: "0000000000000000000000000000000000000000000000000000000000000000", 1284 | valid: true, 1285 | }, 1286 | { 1287 | scalarHex: "0100000000000000000000000000000000000000000000000000000000000000", 1288 | valid: true, 1289 | }, 1290 | { 1291 | scalarHex: "0200000000000000000000000000000000000000000000000000000000000000", 1292 | valid: true, 1293 | }, 1294 | { 1295 | scalarHex: "0300000000000000000000000000000000000000000000000000000000000000", 1296 | valid: true, 1297 | }, 1298 | { 1299 | scalarHex: "0400000000000000000000000000000000000000000000000000000000000000", 1300 | valid: true, 1301 | }, 1302 | { 1303 | scalarHex: "0500000000000000000000000000000000000000000000000000000000000000", 1304 | valid: true, 1305 | }, 1306 | { 1307 | scalarHex: "0600000000000000000000000000000000000000000000000000000000000000", 1308 | valid: true, 1309 | }, 1310 | { 1311 | scalarHex: "0700000000000000000000000000000000000000000000000000000000000000", 1312 | valid: true, 1313 | }, 1314 | { 1315 | scalarHex: "0800000000000000000000000000000000000000000000000000000000000000", 1316 | valid: true, 1317 | }, 1318 | { 1319 | scalarHex: "0900000000000000000000000000000000000000000000000000000000000000", 1320 | valid: true, 1321 | }, 1322 | { 1323 | scalarHex: "0a00000000000000000000000000000000000000000000000000000000000000", 1324 | valid: true, 1325 | }, 1326 | { 1327 | scalarHex: "0b00000000000000000000000000000000000000000000000000000000000000", 1328 | valid: true, 1329 | }, 1330 | { 1331 | scalarHex: "0c00000000000000000000000000000000000000000000000000000000000000", 1332 | valid: true, 1333 | }, 1334 | { 1335 | scalarHex: "0d00000000000000000000000000000000000000000000000000000000000000", 1336 | valid: true, 1337 | }, 1338 | { 1339 | scalarHex: "0e00000000000000000000000000000000000000000000000000000000000000", 1340 | valid: true, 1341 | }, 1342 | { 1343 | scalarHex: "0f00000000000000000000000000000000000000000000000000000000000000", 1344 | valid: true, 1345 | }, 1346 | { 1347 | scalarHex: "1000000000000000000000000000000000000000000000000000000000000000", 1348 | valid: true, 1349 | }, 1350 | { 1351 | scalarHex: "1100000000000000000000000000000000000000000000000000000000000000", 1352 | valid: true, 1353 | }, 1354 | { 1355 | scalarHex: "1200000000000000000000000000000000000000000000000000000000000000", 1356 | valid: true, 1357 | }, 1358 | { 1359 | scalarHex: "1300000000000000000000000000000000000000000000000000000000000000", 1360 | valid: true, 1361 | }, 1362 | { 1363 | scalarHex: "d9d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1364 | valid: true, 1365 | }, 1366 | { 1367 | scalarHex: "dad3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1368 | valid: true, 1369 | }, 1370 | { 1371 | scalarHex: "dbd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1372 | valid: true, 1373 | }, 1374 | { 1375 | scalarHex: "dcd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1376 | valid: true, 1377 | }, 1378 | { 1379 | scalarHex: "ddd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1380 | valid: true, 1381 | }, 1382 | { 1383 | scalarHex: "ded3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1384 | valid: true, 1385 | }, 1386 | { 1387 | scalarHex: "dfd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1388 | valid: true, 1389 | }, 1390 | { 1391 | scalarHex: "e0d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1392 | valid: true, 1393 | }, 1394 | { 1395 | scalarHex: "e1d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1396 | valid: true, 1397 | }, 1398 | { 1399 | scalarHex: "e2d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1400 | valid: true, 1401 | }, 1402 | { 1403 | scalarHex: "e3d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1404 | valid: true, 1405 | }, 1406 | { 1407 | scalarHex: "e4d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1408 | valid: true, 1409 | }, 1410 | { 1411 | scalarHex: "e5d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1412 | valid: true, 1413 | }, 1414 | { 1415 | scalarHex: "e6d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1416 | valid: true, 1417 | }, 1418 | { 1419 | scalarHex: "e7d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1420 | valid: true, 1421 | }, 1422 | { 1423 | scalarHex: "e8d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1424 | valid: true, 1425 | }, 1426 | { 1427 | scalarHex: "e9d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1428 | valid: true, 1429 | }, 1430 | { 1431 | scalarHex: "ead3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1432 | valid: true, 1433 | }, 1434 | { 1435 | scalarHex: "ebd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1436 | valid: true, 1437 | }, 1438 | { 1439 | scalarHex: "ecd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1440 | valid: true, 1441 | }, 1442 | { 1443 | scalarHex: "edd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1444 | valid: false, 1445 | }, 1446 | { 1447 | scalarHex: "eed3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1448 | valid: false, 1449 | }, 1450 | { 1451 | scalarHex: "efd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1452 | valid: false, 1453 | }, 1454 | { 1455 | scalarHex: "f0d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1456 | valid: false, 1457 | }, 1458 | { 1459 | scalarHex: "f1d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1460 | valid: false, 1461 | }, 1462 | { 1463 | scalarHex: "f2d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1464 | valid: false, 1465 | }, 1466 | { 1467 | scalarHex: "f3d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1468 | valid: false, 1469 | }, 1470 | { 1471 | scalarHex: "f4d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1472 | valid: false, 1473 | }, 1474 | { 1475 | scalarHex: "f5d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1476 | valid: false, 1477 | }, 1478 | { 1479 | scalarHex: "f6d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1480 | valid: false, 1481 | }, 1482 | { 1483 | scalarHex: "f7d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1484 | valid: false, 1485 | }, 1486 | { 1487 | scalarHex: "f8d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1488 | valid: false, 1489 | }, 1490 | { 1491 | scalarHex: "f9d3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1492 | valid: false, 1493 | }, 1494 | { 1495 | scalarHex: "fad3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1496 | valid: false, 1497 | }, 1498 | { 1499 | scalarHex: "fbd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1500 | valid: false, 1501 | }, 1502 | { 1503 | scalarHex: "fcd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1504 | valid: false, 1505 | }, 1506 | { 1507 | scalarHex: "fdd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1508 | valid: false, 1509 | }, 1510 | { 1511 | scalarHex: "fed3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1512 | valid: false, 1513 | }, 1514 | { 1515 | scalarHex: "ffd3f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1516 | valid: false, 1517 | }, 1518 | { 1519 | scalarHex: "00d4f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1520 | valid: false, 1521 | }, 1522 | { 1523 | scalarHex: "01d4f55c1a631258d69cf7a2def9de1400000000000000000000000000000010", 1524 | valid: false, 1525 | }, 1526 | { 1527 | scalarHex: "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1528 | valid: false, 1529 | }, 1530 | { 1531 | scalarHex: "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1532 | valid: false, 1533 | }, 1534 | { 1535 | scalarHex: "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1536 | valid: false, 1537 | }, 1538 | { 1539 | scalarHex: "efffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1540 | valid: false, 1541 | }, 1542 | { 1543 | scalarHex: "f0ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1544 | valid: false, 1545 | }, 1546 | { 1547 | scalarHex: "f1ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1548 | valid: false, 1549 | }, 1550 | { 1551 | scalarHex: "f2ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1552 | valid: false, 1553 | }, 1554 | { 1555 | scalarHex: "f3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1556 | valid: false, 1557 | }, 1558 | { 1559 | scalarHex: "f4ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1560 | valid: false, 1561 | }, 1562 | { 1563 | scalarHex: "f5ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1564 | valid: false, 1565 | }, 1566 | { 1567 | scalarHex: "f6ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1568 | valid: false, 1569 | }, 1570 | { 1571 | scalarHex: "f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1572 | valid: false, 1573 | }, 1574 | { 1575 | scalarHex: "f8ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1576 | valid: false, 1577 | }, 1578 | { 1579 | scalarHex: "f9ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1580 | valid: false, 1581 | }, 1582 | { 1583 | scalarHex: "faffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1584 | valid: false, 1585 | }, 1586 | { 1587 | scalarHex: "fbffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1588 | valid: false, 1589 | }, 1590 | { 1591 | scalarHex: "fcffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1592 | valid: false, 1593 | }, 1594 | { 1595 | scalarHex: "fdffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1596 | valid: false, 1597 | }, 1598 | { 1599 | scalarHex: "feffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1600 | valid: false, 1601 | }, 1602 | { 1603 | scalarHex: "ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff", 1604 | valid: false, 1605 | }, 1606 | } 1607 | for _, test := range tests { 1608 | scalar := HexToKey(test.scalarHex) 1609 | got := ScValid(&scalar) 1610 | if test.valid != got { 1611 | t.Errorf("%x: want %t, got %t", scalar, test.valid, got) 1612 | } 1613 | } 1614 | } 1615 | -------------------------------------------------------------------------------- /ringct_test.go: -------------------------------------------------------------------------------- 1 | package moneroutil 2 | 3 | import ( 4 | "bytes" 5 | "encoding/hex" 6 | "testing" 7 | ) 8 | 9 | func TestVerifyRctSimple(t *testing.T) { 10 | tests := []struct { 11 | name string 12 | inputOutpoints [][]string 13 | txHex string 14 | }{ 15 | { 16 | name: "be9d2cf9b473dbbb2c59ffb07b5d812516f94d64121d87ad61956386a4bc3843", 17 | inputOutpoints: [][]string{ 18 | { 19 | "edfd415247d3e5e6eb4a7d65d4b25026cdea1b5595f0ea42ed56b8e640310169", 20 | "8d073256bbfcd190b56c7e2d69302bfef0f2dcfe58c04349f95f3ed8310e9ffe", 21 | "38ea121a03ab1bae88357a88cdaa5727f6f5cf1d9e287efe33571ba9e2bc9ea6", 22 | }, 23 | { 24 | "c9afb7de7bfb5d9bb5a593c969b4b93dab59db4ca05e20966b023fbb2cac4f88", 25 | "12c5128ecde3c358528236ccb62bffc04a83471802eba04d94ec01d150c4ebf1", 26 | "aa5b77ea8c2d23bf282fd20e70c234af618769f22afbbb100f084bd25ec8f1a4", 27 | }, 28 | }, 29 | txHex: "020002020003eeb724ffb209dd81013006bd371b1f0896e8288565a6ee338cc1bf8d49377c4ee6fb4edec941b469ff020003deec249bf008df50c4d6e57d433bea03e84602181ec725b2a5e26b39f690f34fa4344cfe5e0af6e6030002e8043835f159904ba847435bb264b268bccb72182103e36fe107c667c978c5c200029c9fe087f1c89be13f2b350678054326f83732e0c0ee3d1f679f6007b5d7d84f00026276a1056f82a31f15c8f417b47c7227e89f6084644f21dc84a6935eeecf19a5210111a67c084c7c71d2ee4458d2d7f213ae6b79a00147e3d3637adc084b098b5fb60280aef0815ce1848e3c587524f501f927d7dfa329782ed9b12447bd6c5435e6d4380c61394eeb91361a6c60f8a1414a3f001a3855c1e881348588ba5c4c0d8c5ffb387f9ff6646682c579ca9bcd82dcc79238bd3ac9cd1d7d95cae4f7e51e3a959e9cb8390974283a0bcd7022fb79d56c48456b7173f511d801116ac823d551ed740f866a06d07a4f86404a55f359659acfad67614d5b8c3adaf29cf07e0198faee3683100e0806eaa766dbaf09c93d577503e9eac4e7d1850dc9c6e8f7221be13f1c36670bdc96e7e6b973d409d5ea3f81972b844f083db6352c51def090cf3e0c2082100d30d5bf7f0602ae45eeb8361c0fc61d159e2b8581871d936a8d70e623f1bec102f63090f4b6e620fff2749a79c9556ee03cc02dcdd7200379e667941ef691d490f615c8405ab8b8024eb7f3b8a1f5eae543b0122db8321785556063e36a892f21e4ec30fd051afad9691d759f12aa5411c14257f8fd3caecb0e859dba3516d0a3ea16669bee122c55182a8673566c6afc38c93ace2650ce06b41a14b62eabea08a99c3b8b09f334b9bda5906f1e481b3fdc41cebfd7c45a139e8c156b68bddb04a7f7c617fd88e2bb066abfb91cb9c708c39ef6005b37a7f3472357dcdc500f039b15c7a871ea9c975ae1993345d9a7536bb3d8f1fb9d764643b354eb11f788021d295e331a5c60cd5ea56b85fc18840037e9cf6d25b5f09a9d99718a260ce908464ff548f0e8a247072aebd17e7b614949794f46f38b79c1a506ca6d567f0d002f9c9649eab343ad7da5755118623f8ee5dc628ee50f1a5d02ee13bccedb080aab365eadd3125fc8297355bce4c32d7eaef730961e958fbb6bbfc4022b59f909bbe7b7fde33c9f3eb0fb87531f8b24f6c5fddf12848b7539d4cb4a4ed68a2c093cc73a3c71e0992873f66030737a3d10d276678c28144e81fb40832d52693c07c96c24f680a627a8ea8f8d76f61f9403dacefc07fd3187102b32d4ecbe5bae035fd85b6ddad623ff9e081890e15d47398cab231cd40b2a94da28f65a6c36e20861c5a7af0cce66128eab0c2d8461c47d3a30698110be15895174b72e0fbe9e09ddbb9325806e682a6bab52608c37b292bd02836c8188751b13e09a21c0bcf607595009f6d1d5b8940f94a506ec58fd1b79c353855e0db2ecf3048c2f7224520e72cf02272cc7dc999f7675fa3269606f6be3119147914c73b1c0cb779fa3a60f144856042cf8a7d930d5d74052042a2590b2232383ee8fd44e63453f37549f0c50a6358f077cc7cc1af7e1515f9efda750f7887d04ffe728b1f1cfaa63da0f0aea5848a5871a9617edd9d414c0b249fbe9f1103a17fbc08d794f1c7660078100c54f3700d8df0a3c5ec1447694179039287ac8c392e884542a0e12413f4bd60c85aeac6b7bc0b51a419b3291a4f1234db9648b301e966b723aafd2381c45f80e3ba5ea209db729330091e03eda426bb08c6d0d15d8a31b2aa1470b1ec2aa9b0f4890bce254b5ffa8c07128d0450a8533695d774398985ddc7399db3472592605a58b9700066a060dfe2ae6681eb2ea195ae87c3859d1db28a12e99f166d2f508303395a4145f85a5c6e21b4809d4e95af7e2569ee835af71675486581e443e01073a7ab3ed80c083cb628447450fde4ab33e19c35d406b46dc2d6c04d24f610be25e852ff7be104369072715eb3098cfa4182a1292576f97eb8eb996d0f9800c1eb35876e019ef8b1f3f4f0cc46b7f25f50d0df7f53451fe17a8d245e0cca0022449bd26846074a351ea000fc9393f56022869498f2dc9627fb710b50ed1a5006e235da7eb3d8e930e7f9be82d16e9bf99d5e51b5067342f104832b262e6b8046519e6d914c33ba037fd94d1e37a251597e14be3a4a50743fcc56726a1229c030bb3bfd3015fc95ff5a128d586d783e68bfd958e63d83a9daf700ec22492cf06acf546097351b35ad68ba245abf4169f99d253f616c42819218987fffe192c0e9c7f2bb74b31e00ad302fb9cdd8ee3ce2bdf38bcf2bd76921c780a9180825e0e40caa0854b19ed6f007a248517d244ce34ab8e0dcd7ebf18a61ad2d0e2b7330fd35df106d1cc436c318a6cd88c0aed3a7aec4307cb7e6bfb99923ef09a71af07536f39db13e261f61221d3302f6757214b0c33aca327b658d659c5d00d8e5902bf773d09c204c941789a4168200fe897a465e8c30020f98cb66864f98f9a46064520a4ac94ffaa29477b6cfc5ada48b4d422e64f7768fb0aeacc15b5c0038c070ffc28941673707c3bc49d3979364a0d50027a836a910390ce80b651c2fb47041ad40c202a3f982db0502cb834ba3b8642c2e3395354fdd443227f954063390cd229b6e84d0c579ace647f83d0883fd77628c1329f7942f91c0ca1a7916a720c3491f23a7c9f0ab21ff2ee74729d38469804894f46fb6cd217af31618eb1b002ba4483a786b9241325c84ec7c37e9ed4748d2f802a176d156f293f05e3cca10ab6ab08b5ca156da77f5010c78d6c237984ff550a7ebb9943b3f698bb8d61e80ea5b1135aad3d386a6fdbfc0163517e5eb7af9d926819baf5d9166298c40f100ed71d97e5beb3718421c3727fe5cee2a2187d829f7005156d65aa4c5fed4d0f0aa82cfd5021d417b53a64beb05d519f3b39b509c02b7ab6b9033f195ac1c4ab072c32e208a70b20d554702480b9a344b7a44126e744abaa96f1f7406638a1b6006d7765ce6a9206029054246bfc2037deaf59febadf7e3e757f753cab2465b208ef8a5ba15f3798214a26591950ba3e2a737184afd6d98f5f41c57cc6cd9fc3032c4ce600de91f1f87cf360ac60742d145124a6e70d5a4cdc1dd15fbbe4f612011b72deb21f734273a9d558a4098b1194dd0e71b27268f3c6cb84854e79cb7300d5e10322648535700994811bc6a96d3ebfe84a03546ef69ad6aa7a96657d4d09de424347a2541cd611205d4e229154345cd7afc82b9fa424c173e80e37e5320162a77989ee8f120f08c58edd72a057e07c4c879027462c44d4c1f3de0fa90c0e31df418ad8724aa58adc5e4452971e4da973acc8ac88dbf092b05ec33e9e8606de4584eba088aa09bcd2ef952e498a4c5ae3bded5f6f76c55901d5e67be5840971360ff2e0317afaf855d91f67d7ef2673d912f21faa9bda118a7ccb8f4c0e005d725c22ecddb8de034b86f2a150f522bab5e3c8880648fa8a56891bcad2e908c4f0fb1fc97b1b0523c70d5647624ce29a968ad2aec976cbccfc1d3770b3b80d9a5481a095ba7ecf5edef11c258cd3ccf1e2fdc2f6e6699fb382950acffc8b0c9476e9840debb9e5689a0e44721984970f446634c572a4e5e4651bdcbb1eea055168f8c390fa90def1db63a2cdba06031323d98bf475b1c11fa0180ff989e80b428a66e88a572fb2ce9bb49dec2ff12721ebfcfd805901d90ce0f12d0b58ef0e7747adb41140ebb51f705cf6a37c1f202668c2173367245ec692d941835b6e07774a58bec6a0a40603b9374b4b85c1be4b6d3e0595f2e0e4d2449dc98c164d04f0f9cdffc4b5fb42dab130ddd55891a7c08aee1d68755c87132b5784a5cc030dcd13acda0edf0f8927a6752cd287e3eae2ffb185cdcf7cc5688141255c9146082ce3d79db8735c29fce4430295f4295185c2acef84a38df71ebe36930df45707747c28c129454f8f604fe628e72c45a8954bb90d02505b853e015681da4eba0b91dbb1a2e30f2d539e063cfa6a1eef48ab585fd4e6d9626075783a6f1a47ca07a03c24567891eebb799c29e33d5739bc03c4080a51fe2393878b8ad4c821380343c952592cad103ed3f99383ecc6192ec2d8748d843d869aeb845c224198360a453355dca4b84785b3379cff04899386712f2bce9ff3a8a5c09b3b6104053a0965734ec5d9955c3efc379549d7c516756bbd3ed7b553c70b517c061ffb25fa0d44f2d3c5d6044fc32ec028021774d7068cf70e903466bdc329e687ff55cd340cbd38d5d6d3ca60742a013d47d42dbd3e4b55778c9d198fe4ecf1b6901774c508d92457bcd4605bf15b075c34c5d7fcea39097a56e6dc6ce30d750ce1388f3600fe5c1d514f7c1eb45490aeacce197072aa4289f2ed9d400682619271dab5e20c89e7123202a1d5dbc0ed27e5237f8c8920fcca8d4903c8854225679991922a09782c829ece65d5d907426d97d3a32e3e3c899fcf23c35c5609d297c48d6f5c0fd09a5b45bfad4e57db448d85865c60566c9d73d069506162f5ea5b61881c910ffecebb221af9ed57b321b89e9973987b8c2c3fd11ce26c395cfc45e2793831051603c558ddf8bcb6bf8309fff217cc3cf064f86d97e9988a9beca295bde27d0e23b33c05d20cb48107d226b5673bd498a83a6da07f9ae2dff7cbfbdbd08db50bf0c660a58af1335cdcc7dcd155f49a889fe4e7fc840adac45659cab56f792207e5e406bae5367e630f258718a0cb9524f7aeff2fba421f2bec888baf64f1a2079f2d87f20bef4a30cb7f6e7b35ff316a576c2f54767e15ba4ce80857d918d202254b49a0a062f7772adfbf2bcedbe2cbb145aa1f9721d7638638c2e752a53b0ca55ac2e537a4dcf17e13bebccacb786865bb2359c6197f7650fc5cf5eb56eb0e81015a8322f912dfbe3b2be2b6281a0bf07ba9e9ea5c2de95abffd3e1f9f440158b9d5fa06bb8bb363f82a61bf7b63a349da071f89301453eb6fb0089ef1fa07d7cc99166e9f32975f9374ac18cce9557c2fc029c6af1725c54a398867833809a7f7151b6e5a695f15d322ee9efa380dcea0092fcce0c3560ee08595897569096823962514330eac2e10bbd15b99f2e0df77a32ff0525fc1b1da8d78106bde016373582c7358c203f42f0cb14dfda584cf61a50a79aad217ecfcead6ba408609bf9484b867697fa052fb2a1024a285ea6c468b8c1958ac614b8378dc1374f10953f81ef9fcbe91d7508e09457ad8e2b6ea4b742368cc2fd732f52bd10369690f9ec6ef59a26e2edf53249cdd0537d851d6a4be5e16f0fbf25677983a9e1e7a02569e221572ab021b9feb43256ae6bf145d0a99347994af4cc71d01fd4e27e80f84ecb3902a0ac85be6fe02d919dbd8098bc4c6ea1d5c76af2f45ed2e645dad0802b3dd6e9789bd9994ea081a0f9194b0a0059aab9ddf048a4302c5e9cd24d20c2d2dcd3e1c2da87b0f75cc94e98f360ff28d43368ec7f38dace3c8808cf4d207e9acee6b0d4118412eadba5fb30bb1887cdcad039f13d797aea1979989c78807c728999ed1f323602940cc2e1ae9a1f2d75ca40b45fe734d6e939b0915b61206b24e110ec8601b634e34ed9517602301da8feac7fc02d5d788491a72c68f6e035013b586a8a59de99d9c4f5881f4f90a8b9d52c49e3639866a5cd0eeb72ced08d4dc7b5a22bb410c0621a0e320fa6a71e071586e63c95daef4885e8ba30d9e091318d897eba826d361c3c1ff4c87765d30c6f1963ba25ccbec6bca28dd3e420a99f6bdbd125624304d49888f08138e1b865ad8cc933c46dcfe6ccc6a4ad97b025da8baeaed55d4e070cd06e49ee2b1442d1a3122e11bd19b7401bdff9335a702e1c426bb560eef0a72e95b7f391e2fbae1d55ff33f54737430b64eaa8645f40abfdf1a64a39d26a9b1719b344049aa39ff5428c8abc8d24f16f7a7365c26be0401f679cd16257c8d02151f5f85c319ea2ddc756a48b21722e500b0260c117c0042b5aba2c4dc66397998637799c3ee1a56c8cc932d9b44cfdc194a4306565000f44fd018c3049b99fa8fc250621ed566d6c701391fd537b1380a02380884ca00889ae696745a48bb3e900cdfc744cbd0def9dad9d45f30a7ebe6f385bdfbab0b44260fdb8682feb4fda0c7f23fe11b5524d1e4ad5752e6a82224658afb73ce0c1bed7afc986c0d77fa345bff8b775356d5a7d243441db47419c5538199253e07cba7eab3a8aa961e9eba40011d52fd268900135bb1e23837393e492f5e79fc0da0d18985295b0ba4cce4a1652e9bdae4c7d9201ee0c71da389829bbf2b0920000b1958330e558627418250e1920187b6fa7d5a98275e85537c8347203ce26b031cf2d67f127f0f49ac9c3577ccb85626bb7e2bc297968f99a46478535b59eb030cae349502940c272eb67aa17b27d5b5f98343f763506d371a82a8d89b02830193dc887380efc30c914973a4c3df231e5218649a871ec8ba088e3cadb5574f00f6da7420baf4c343c79786715c9cc6c612d605040fe92a62fdb93d2319fa3d012c13e87a3383022542f4d6f881b3c5666b3564ef4abd82f64f2a7be0a15e500cd66dfab447313fc359b3bf4f7828f12f3dd8e023860ed061dfb0054eea712608741d8c00041a56f42f356be5d112dd554844d2d9daeafb15e1b4aa54f9811bc2f7d6bf6c3e19f5dd448ae9e5464601b8129cbdb2f71cea6d712a042deec314659306f4047432f827793cf10a240addded048463e1fb65add1cab4422ee98ffc9a8f9ddfd31a78ca3dd7e0cd74ee289cf693cbcf308bcf731ec62ddd8e851adfd2c3f3b5f2dc6c8103aa50fcffea275dc56fd00abb3e12d078557659dbe5217588198724953724ccf5857dfab5529c74fcead15df94de90c57df3d3c048a1137ed0f2df83df489a60d21279ac87750632b55c93493a4c51e7dab476c5ac29423c134ad1a4b7f27560ec97cc6182f3f349bdb2f2da0a24264fbc723c0850350b302bf39a341e5056cd55631de2c30195b2f87ed5f1051de877d124df83913a747713e0f4258a6a2c2be1fef3f1590fa63ad5857ea386889bc42784e037dc2e72442c5b2e2a6c02a0c7e5f0c65fb7865f4094c40910e88866b759a6cb1fbe5cd83efa1f93ec654ff254f9ecfaf5ae3704d3a51fa5f771e8db3b822fe3f0f3063d9c1c8a460e7ec8159d0a1b300d6369448dd6a249aef77dde1663fefc6f1db67204217f296bb62fd3403f7470ecbc854e7da02d3b58efe072cb96d7df1f4ed95c2d71892321543b9ae9a83871850267765f0d3df67fdcce6c0ded21eb6d00357a5b09d93d646a5af7760d45996b67b379510b6ab466657d014e171936c4dfb1ddbe6bce24ed78ab33a22cd562eaf86c3854e33143c2ddfad4d991b060a61e168cb48493fd96b3c8d3a6a84df5900b6f762f5a4f783af9a923bc4fcf1d423a2dbc395807278f9c41e317d265cd7d0d0d4f844b07c6991865226a1b9b10b33a05d818f0ea0c5c23195df0c56b79d4ef555a906d9fc70b2bcd348087ae3435aeea6882ae2778fc4eeb95965f0f309375d9dd7b4689f4becdbc68b88e432780b3da082e15232e43112c3f5b75d88e80fecceb74fb13aca9c0b9280d140b2551b2f7a9783ec582cd97dec62c3ed831eef3fd5469c2209ef3e1c0d230eb8fc49971e627f588b04145534b8e095fdc6564b33e78ae0ced0efa355b0ca30ff2b8cb46fe22329dd4e49ead6fc09476d2c187412e15e1c607ea5b1ac92853ef44bf55d2119d10a4d53a3bbeae2bc26314416f98dcd5d05a4d672de90ab2903367b0851a2e397b3eff98e51a4f1d7b76e11db06d3422c7f9a13dad93f1eb5a45d7168e787b808a0144eca91a1eaf95832e3bd48c109e68e0aa02d3a055ace4973906817a61c373c4c3d7e7c011a7c36d7872ff4b9e52f8be4b2dc18546dee76b5a4a27f204b34d5e81af0df91507975c64c7ca7d8308b6a07cabf0b979e4bdba1eada0b71ed162561d6b6481dea108f46c000b4faedae3971fc0ead00abf5d59b0af7bb2dc7781cf271b8b7130b55fafa1940f2542eb01b8b3c19cf930be56aeb7a8a1f975d502d9dd9499641ac067efee8bbff7d87ea50a3c575899e7e35fb687f7857b1e213a8476980a2bc04665e3aa778614a6510cfc51777dde2d5c3dde73d59b319bbb90ae7e2f2ef4e6731e50fd4c2c8128edaf3f1b1a2eb68db169c019ba882ea0f4eacf94ae3b9aca31fc714c7bb42d7677afb5d89cbc1448e78a9288f8828e27396dc7be417e9979d6bebef552cb92574d2bd3b9e615baedc21359a19a8aad4c11dc525bfaa02230cfd9c47dd2e36cb54060bad16e5616feb0b60c2f376f652c78de37991832212028480fd654191dda6a6183e5d32c49145837ec230a27d3b9f9bd11adb7484db8d0e84a59814a2d32165c20aa6e5ee1927dbefbaea3ef4eed8fbdb5ebadc0c5a5ab6748c3141fb3c177368e40642275bde104743b2c1d04658b4f99833d8f6447ee07009318463326f838ef47bfbfa2cf243b769b65ac2f5048a9f51ca1f44ce3fe7186aa13821ce6fcf93ae6fdb33ae4bad5fbf444e3d632b6b7369fc26fbafd3e6f88fedccbf5fc9f9e31f0b4872c839edc3f3caa40a1188f6f90256792811587aee465e6d3fe1c6e2c9eb1ea9105e0aebf23b9b7a5198b1577ffd0d843f3450608ab52372a0512a10e1727494a41e7be75b36c577bed08fdbd7402f6eb9560b35103b0db52ca07b3cd9cf6785d00c3b1127947562989424a3ece30565ac16a23eedfa61e8dfee3e0e1ef3a25cc8bc28b2ce65e9ec13bef65a67b65f42ac0939891bc8153f5984e5b8be8a4b8276dfcbf3d723f9a7e0511188be7be2ae3f8d32cb54bbd101888b8bb201b2b7aa0e656ef80e4a030587d625f4e4395df4a8f8dfd0ad2f4e0d9b00a0882bda507f4e0bb5d351e4258965d0b29446e97a7022e90983714bcd15b16eeafaa9ea50d4087803ba23a33a71df4d5b338d5e74802c1808c0f52dcf528cc16b558a14bc6a20a71ea5e658d401af41fbc4083759553516d4cd6beab747648680cdd94fac4c094dcad5f1cb36198f33cc2e45a6d4c12db6b51e09e574e405c2077b518a745559e3a4f72648a68051f00d28739d2f961f22599fe82f5a9ea5ab53aeea61fef84b160ee6f49c60e9bec72231de7d7c1a745540712e2c14781dbbdee83fc2f8390d51f522496254be97343cb5993a030475acc1defc3a7b274c2c80eb51ddf3fe045e472e162cab6cf46f2becc5d82dec92409dd097fe5bbb19ef5a40b796a9ce2f71956d109b87499cf5c57e31f67b9087928f63fd2921af5d869e105fc19acafc9eaa92e512f6740c441fe576c0a322edb56fae2b83fa4f36f9e15a7f9c55177552acdee1d82a2d75974923a5a913a92952ccea4718e81f2894f2266878ff2f5dbd7adc7968f8a5ff3615af7c013068fb21947793563c2a22e4f2506637a98ba43bff7154a0e81d544bd53e023ca63cba83adddd150eb432945808cfc5b707a8b4bf76d7a9153777220e251dddfa4fa7b579f21b6e5a2277e1ea3f41cc041c815ffe20ace553169160c8e50e2adb8f472611adacf1b72906d8733e8e531ff6d1a4590c1927071ad9da8e540087b72f21a9ce295047e88de99f09912945fc1f8fb4b4e46c4c9c24153f35bd05a827a82bdc2b279c88e290ece1ec45612bdbeade56543478a5563f2a2397cf0a8f5f8d31556956ac6971d7fb7ea6b09f003b0780700c6624bf6c2ae17f047e0eb9341d3e471ad204773fbe26fa0b0844c1fb55426816161e46682673c192f807a751cd20fb673ba602c31473e0a806465137cec604e5c8fb0aad1c22c402bc00663ad679751d5bd22c918c73365365d2506986eab05ea4b493fc1f362e916f02c7df044eb775a8cf15f2af7cfbff6e59456b5e62e990ed88f54effbd3d33740ca6197d0934f2b25f404a33dee98244b82566f577d05033b54945971eedaa0706b01e233358a1469a0e66bf4494c18c901db26eee0ba73496eeb1a1b7665e7a010b6dfab72543f3f5da3d228f5c7cfc8e368f47cd5052102838fcb471b67691023ed28e08c33aa1214c6418dd14307a50613748064481d29dc157e5e0bfd31002f163f612a6bb3f0c10f6e4db2145e52c0d3c8c3b87be5ee717373674b5be97078d05ff36f517c70caf12f75b5d8db78efd997ff16aea0d40b621586f5253b60681ab45216061119a4e0b5e854efa5793d1b0640126e4b3e6b86c6452b532e50cea7250c5b91ba9d280837ec629aa7aad27f3308dd1a93f46cceb90b648131c0b7aef37c58294db0db1dd86f962e557c9b306784196dcb89c3b91830152167a038549c28a8f251c51e7e7b4c20256a15455ab3474bdf9358422192e83a8557505ec7781cc474509dcc2e3daf1cbb8fa5849b6dc30f8802c758e144038f34eed0912e162bc006effa5d45a5acc0502a04d86ad4e9928c38c0b0bfd81df4eed3e084e06559aee02ed4b93af956d5f1acde0b5b002b932e90aaa14a5bac05323e40f70020d443870ddf0e42cdcdd8a1611e7240a209b836ab7599c984bd69311100a290cfcce0eeb9f22e6953ee84d93f668d46ae9dd986fadffc1dd21134b09e20fbcabef1bbd770697e798cb5123122dece399df388256669b326b05c0325feb0d6e80db9b63f941e26e61e7d93e1248b67454e5e44c86826d50b61807e0bfd50ddf2e800dff97a3e49824163266ab4601df8f81ce7fbe89f9011abb2d76aaa50d2f39d787740665a841be746d6426c3208323a9c522dd7491fb1b1ebfaa2e240af36142f584853d825d63f31c876e8875dbe72043501ecc83c68b619c124e930f6e4145245d7442c6dd3607f2b2328c5f6abd8d09554232cc3fb3fcaf7d240b0997ac61c7fb1b0b5cdeaaa0024086046c793d38c6c103e28dce4601b437dd6b00a27a8a086b86d4bf9b5237ffed7eab2f1a2f4c7912d16f7de386ca53eadb8d0958fdf65e2781e39ecde8355e3a3e885a7a3099971e541a7ec822caca02691d02a5ae37b8eefb72ea430ec3325851bde3e0254b710dd63808ddf2346239ea86045e08d822002e04f85c597d581a64729d39b27f6818f8402c6223ad5aa1f4eb0ce4e25e8d1a13ccec340634a29b9a4ac0c22824654bead61bf8da0c7ca46bea085ed7a4d176d7d777ea9d41e6f661eddec9f4a931aa7b195851631860fa10490f4eef0aa557a57fd351e33b71b01de720963a00872a2177a3f102589cfa39af0f04238be5c8b6c53e1e361d9241dafaa02503ddfd996beafe4feb989f5195330e0e33fe461cf05335310b2c97a73b36b3226babc44dbf5134f256d03c7386920b9d39afe6652bb4833c2c21e195d396f7f67452685dbb119aab7f1556a7ae9700937130c4d3ee3a773f0a69a0303dbc845a8a885e8200221cb0971414a12101045905c4175f70af36565e20bfaf81797d95db17520ce2eedb6a2949bf94d70106803172ec56734ee33541f304afd8539d5663e19f06597f60ee81416dd4acea02edb6d1d2511077e5c5c30094cb4ff7000a55a3d323b97dc6e64dfbac4dd1ed024e7ee205bb7cfb3419d76c442c779cd972101c93708ad59d5d4467ba74480a04c8b5d639f0f1917443c3119ab32b7987eb39bf40cee8dc96aa16b3f22ea8220e9425ec1af65b890ace9a55cf8a80550536056793f990fad922f470d6c142910be061d758e6b65f8fc2decf53ca47a5a3ee7477e0b6ca777cd9591eda107508054d8e409cb2215860d76d613d5da0575b783047691594a2683b012a82c7ce2c07b6ac487e25697fc7c08ef75fb73f40a841e721a9c3cec03f6bf231d52e15fd06a6b6e7dc13cfb9056b1c7be6dbbb810beca8cd7e5286a21ac7aa4f48d6f75b0e70e8d0f989f92da1e920e57b762326cfb77763679cf674f514fc011a14a34e0fef062713f8ff6d71d53d9fe1ca1a8f9b07d1f18da95b2022d303b81ed4c0960c7f246f06af8d8595d53fe421b50348daab2aa056414786dad6759bd1d31bd10ba266e53b681e85b796b00321af39bb3242245c5801a15926bfdf783c6c14b606f02a4ec21abd5f4cc10b747ca91b15ae0712b780f3aeeb266b5f89e90c0ab7090ea4e164888a88f22b1e9ab3ae320e4ddcc67d6f2be9b4b8753080492dfcb008c87148ca6557137ac56e1cd65a28f54f8072037bd6e68f5e07020849da595a0f277f8cf04a9cc34f21341ad6a725de56f6b612815c2a9df8a703acdf5456ea0267a60965dba6101e9a1b746bb88fef003d75235c40bbcecac6160b62e981ba09f00e8245c796d5ee698fae8e9c43814aa7a31cf32320d899395c0cc75cae9a0d48f9974dc93705b99ec8f691157497f339b452c8c2542aa47efb557348ecbe0615c9c7e15d59ef77a43535845e13fcc12e719369ecf49f397c774dc3009d2e0d5e185e352baf6cce06847598b1661e83f10ffe989a6be95ea2e32296761d5d0bd115b9a4d236b9ef95ec839dff67d503ce563718f037a95e2b74cfed934a8e0d7d8d9e7e850d40256157d026dc9c97dce1f4ff5cc75e3268db840102f431320672fd210869eff22eb11165ea3bfdff721c3dd9da0ffecd31cdeb1d70a2e38e07f84f72aff200234323fc871da729e6a0816ad67fd5b9fa36b135678b21eb550926015ebcdec6d3e5a2a1b362d61ad64d81a17dccf461af62c1242df1666de207fd73e2d8056ae8fcb6c7d69fb2e7f412ad324c514acef1c332f32a5f674a150bdac408ea1e9b2a22cf447727dd47dc7e99f779fcddc6a540a4f2b517b87afe0d20e7dff2a3c8a44764b7ac129a0c3717b66e0c899fb243f512cd4f3b09df620f57de6d4183f0240b1305e21f45f1376f09f61e7aed72ebb0ad87c21a67e3920aa07f26d8853123574512574b8c8fb52a25e6ce01c182335f4ea2246275fb9b044f235d932110d33074a5cc259e12085900bf15a0a72a39d452f54d8ed342d10e6196dde7ac29ebdaf20f2a7b71365ee276bb2002c29a4be2ecf12f9b888cb304e67399de77e07eb629a468e5fb9a1a6b3eecf39dccc51c84a3463d3e382fa702fc44b7679ecd9fe143db08e10620ce22f167af16fe9696d8691c00b704827f025eafe3252f725e0448a5bd30efa3a0dd3a42a9efc2fa0d106e893ab1cef1f70f28419e55bec0a225937a8236b7895d561911c7a03725f80525a327eab7e68702a0595126840cf32da1924ae5b5a63ffc5eb09c0f3be847d70a65d115a570e70053ccab30905620bfd5729dc44d46d53070825cd0d6d02c144575e9765f89e50bd034b8ca69116e7a993737a206f78dab89a564bd1a12d5ca31d4c8b845e3660dc6261466baf39e6e80fdd60068047354950bf17a2c0935e2f03a6a8a1651b60bf6eabcacbf5399f4fce00d406e78ea9826c6229e619c27f762fdfbc32a2b490c5d90a4bca73b5d8e909a7cc188bbfdf4e591ef66778aac5f0389ae5d018fbb0787ec6b6de0ec93f78dd5f6fad9b918073bdb42edc06b3e738b26dc98568f1f017d02214743b7fabdc0bf8230766ca711e8607bd62b271058add5fc70ba57ae0ae3cbde974be33941a6642857b8a317d540966efb0c3d231313554fe9edf38106cc5048126b612ae74c6b2733ef911bf7fdc028c3d4c120a11fc761da9b73ed0c70f62f3964875b65d6c40ae6697ba349d8c540975335ef27bd2a3a5571117a0b9c46c56eb9d785fc0d984166b4ed4828670f97f385edd5c4722dd28fb0aa820947847d3e1c0a41ad63caa291975364d57b34d4176ee8201038856b2b0f664a04881a39c4766740b4764eefa9e830abd9fa4480242e8a6b99e44c3d5e2f7fca0829d8a7e38fc38e5326277e548816ca8fff9bfd538da74e6016ce0a759cf89e00c5792887b87b7f9f142f179a09f3f1463ad255a7049aca6779404283d8c05a01445b65f58b59ff19d564fae624cf809f47b97a26ff3ba9c02f60d301428e370bcb2ce4096d8f11a5704eac70957dda8f247ca2fbf3a88d39d4639c74167eb708703a5caf07008f1c1e43afe7b640abd2bc1eca80fdc8b92529ca142dfe005903721f05a2c6288da47a2d6b33a38e0c3e92b8e95a057c2020ddb3b84f3823390aaecbe0d73d250a65804920c949b9adf6cc634977228f6ee5f530f7f4937ee60038b79254b5ff0ed115fca8846458c790e05c1e4db7ff0af31576ef57debb8b0fb4cd93509e8535d0242094bd7a5e34d2ffafb4d44c218974b0c6dd63ad17560f3e34eb946fd4c2371400b5b19eb6bf85bca14a820c2a540a845439b03da3e60b3aac4d7b78482607451e4e4ae4d4127964dd51802596b678981fa937610c0a0d7e7b286ffdc42de9b0d15557b31ae167ac890166c0d9dfcb2be4c07c94422602e59b4c72b9e6c80facc95f610d3393e130fc5825a3754e64c25cd7f3265381046c88b76bdb2e507bed5a1d14788872f887858af2064851cc5e758d47a71d4d0798fd422f9cbe041cf31bfcb7c38aa6705b589cac5c20dc38739e8eebaea3310d6207837a8e8c72da07b30fc25b9ec80f09665374266335b3d40e722e22fba709536e2d4d5dad2bf70b91119cf8f41104ab97d39366f42e397e7a384dc4d5df0d50cd3435d7fbf0abbd119b033b51e92f7517cc3d123cf1534fff48a9d1d37d030e88eb48bd9c9d35f73224bf6b620a547ad117323a224e07ae50a6dec817d10e0194a7ffa2ccf483ab1469364675f146d8d163d45275d6163bebe53247852102423f007a5f19fe8465b96ab452f520e223a060885c475553a7958836030af304b2e52e561dc93758ffc8d917283e3423e5126bc2b4c065f31afb480117f9f70f21b6c37373af63c7164229d3f3b8c62f58407184a68209e5cf15816e9545280a59da6695f87bd6e730701126445ebbbaff5174adbef16df384a0a3db952b20041ed8b7d9940eeddb47d9e1632493f201c19ee52d0f381ac2ec798e538da6a00cccd34c94637a9a836b41ad39bf1301bb1fb04c8138658425373208fc535975093dbaea9732fc8099470423abf96766c0a02368ef1902738f684d4fb111169802c014a7bbe008fdd42dfd3b5eb4ab1cd21bc66bad7cbee1c54cfda198545a91093e27318f81006e2d17d67b6258d38cef4737a4d4528ee94bad8ea62103bdd509d7a5836f3651dbeecb9b0061a97e4a9b67f176e299c5fb1cad24e6335c9dfe0b7dd2c0249a97a83cdab9ec710efb9cc7e73e846e8dc1cdbdb3a1f034b8d29c04ec877d426c0ed2c40601583983abac01290472addfee286c252b66bc3ef474032fcb8c0f70d54b783b0a41ac4abb65276627857d34fb6a5834161e56d0fc900bd6d562f70761bf19e73be97dd38d48cba2a827b145400f576e0269d536b34f029441e8f26c1bbe172db8b0f68bca955355818e138e1b593d2a942af7f362cfc1a97762db07c44f9a104c67288eb01edf674fa018e558baf7a77965fde40b10b031aba31e167e9f2796f1bdf132dcd2481318ec44120771fa7c4cac21aa41bff3203a0329735e2d4671f12b20ea2ce748a165d60030b413151d8c263000f09c85ccec7cba4c2ed275116b3b22fa34042b86f5e0907a51569d979edacc16fc88f2dd4c550e16fa1d2a55ae889c988188d4477f49913b1826398b9e3385a3781a55d5f1b564243c2e2a7fef457e6bcbaa510a5f860964dcadac500d279feb0a3fdc02e47d2994d4472c0c14f7eb85c8ba96dce6f3b756c4c8b45080534ce821ab48e79ed6a8d87992afa20cd9c5b839b796dbe89db9228ab71ca2ed4cfa2638d24aa1232e584521b22f259da3df7e328e6045662dbdd8fd94f22fff9360fa035392cc83c2cb882e4cd2264135974894c50d8a8de33980727b0cd0902ed570e7aa91e9f448d01bcd5f47f5f0ef2f0b1d73c5dcd515a920fbc4c4004f1dc361ed97ccfecd307a9263622bc1679093db7e94a1abd44dbde322928c074e40ec54df02ff96b0c5c2bb3cb3f0b0762f2c9e8310a896ae07bdc4c0710ee67ecdc872d0827df6af37d9fa5ec2bbc61afee32710ab22c0bb3c8d3e0e6b04024c0a7a1bf94ce49e440e92691ee06673f3737446a0c2feef4e05e5b71bcf6cca596ff1871f82dc2da91e9faff557b1ec87eaa94b6b12d5ab6135ceb8d3e883cc7a366fb4aa4f0de7f6c84288cf561bc57bdd3d93aacc02fa957d4d9b3da7d255820dc0fd68d43fc84909797ffde36121b6883e2c791f0b0a0a6a4b55de118944abacf13a53b9cfd3e5b51730607c086b51bba8bb5e75a7301dd70cd4f96f8800f3a55ae71ed3dc41eece5a49290e52f7c8d80544e35d644f0fd6fec77700bccb34c88d496443918dc63192ef3425c341cb4e1077e0ba7535ec9ea9557f0441a8cdd2cd75b9aaa8682dee2936b517dc2a88a5e42cfb533bcd843c84447a9974ac276b8435b56d3e0c821f453ccecf4e2eb9c34c1fbda975c5f80d4725c841ef548f8df440a8eaac6945b2da8f9405faf93f08dcce4006fab4ad073ee9b200ef3bbb67b3765a579b52dbe48a346099ff87ed5864ba930ab0e54913cb77734509bf8e7b7feae8c2426c00e859ac1e784bef816b03f6f33ee076cff5b5df52dd3869b886ad25eba7575089204f1c0778fa3ff59f9e10b1ce5a35cdf886ec81414334209912ec2c05d7fb7c3c5013bc49b1a4b303307aa34204a956d17e01e12a0f2148a4d001f32bc455749cc90c88264e913ded95342452dcbc9b094f43ee3426874a09e3817d3d0aefc6819589fbe70bfce4c60e8a86baac80b558bacf7d4881b47035b843fe328832a3603890f66dee09e86a25c6ff9ef2a4fad5576a363cfaa7908e92aba16b31ff13b6fd021e288e3a5b55bf87400b4b137822a6e29ce03ed8c4c75f5d822300d9f66a1187f36342a5d23ca3106ff2a8d60066511dacf4562f6ce3ec8136e041cc54bbdf92b88f3a742eed6a593bf208de916dcc98c4a431dd9f07cd753ea2e11832a05a1293028c4f580225847af81bfdc83cb98854a6b1fdcc1acf4da874ee6cff5008f8d2602a05baa52f0bd623515ed0b6da23dc8717aad25ecb076dd4933638c787e921d99524ffd098b59b156ec7884bcc4091254c7e57cc8c605c686596cb212666abc16b3883271340eb227388b634d8a07272d943cb7ef25b66f52a2c1f389edfc8ca77683f7ab2b8dcb1174bf153ebe52aef84626bafdac37509c11352b5e89d7365fe8545a568e48b8420f0ce3ee4b0d0da4c4ee78b1b7b5da5f466f211d0d1c94c5a2f1cf3df3b9a25b75f970496b6af1ca03cb1163f90957fe4251b555c26f062da8091d9351ce945acc17b51a0f56771e157ce18fd5227c109c2d6d05f547861edaaf46be0fff588e57ae74b3881bd7e67cfa26c173b87e7e931f531a15de7bd80c90c89110db2a44d2a35264857c32a2cd40f7324590c1d5a78213f55366ae144538c65f5f196b0aac00d40408293d2d1f05394d5cea39648b932e686d94c40d622cab355fac4df183d02c1f6b95807c778be3789a05cdf8216b77a1ab6aefe06502072a64a3ecfbd6b2d6bc4eb2d1b309c5afc40c1a7e31d10b21cf770ff7107c38d7f90a3e8ad6fe3605c58c4b41d99d498733d9f4f578d27b6e8489cc800f01d9fec1c4ac856dd6e39a168dabc3b60c60a5980b8d7b5f2d3d830ac25356b0b352a4d95db438667a66f0260af56d215ff8fa95f4adeb41a7b457ad678f935194d03746e8c2e5ae316b201e4edbf3c269051e55517ceee54abe3e2951521e3bb90c065db8ae48731a4a05bcf8c53158b477fa62be05804849726a5ce2cde9165adbec38dd2a6bca7668c5ff5813c570df03a32c7c51d389fb74b8c54eef4345b4d019b92186076731d718d58bf44eac093193f3f881e1775060e5dc32d87a685569f8fef839277f0435231a6db0df3fdc14200af8b83de1afba47da9fd750a4259cd2e16ea5d09a1cf8ecf689777a1d3bc60f5e0637c1d4412ff010ce5baa2eb4e8026bd060080dc90e0bc14a70505a81d70fc5cde8fd9f60efb79ca51f50e746173930d5ebbe57cbe6097e3cfeb85a2963478d9a93a0c1ee1ebd199ca3592cc8a29791eb94761ceb9b3e38fe4410d254176e12c271401ed271c87560fabb8b3ea14985c8d33bfb651cd4c22f036b714c25c91acb41ccfc4dafcc14ca98245ee43d4f36676768c6acd6bf4494626fadd481b50bcab890068588f4e3f7269ec7294e2f9d41258b3711407f150de3723a5627e662f792aad2eb539e66b8eba370127b37168839786d0ca0da4b54cf9a3718352c501e3e08a123ad4f1a09ae0971a0578b4d50ad88b070f2d510b22d46ff57f6086bf497fd7064a0aa98d0946d6cd89f9b46fd83a5a29dfb7437bbf695be2c67f5c083ff76108b59d8afe2c3cf5424606926e16dc783a5b268492a8ba86d157590fe367ef9f0ed2d9a7ef37fb8da8dfcdca89432308f4a45fcb23b90927995d4c2602de7fb5019c181049b9100bb9c9ce1a6a5a9c5528aa4be378d6c25ef615866e6ce26b80000354087ee9f90b04f17862688fb8af1f6bad772aecbd2c7f1a04887c075fb20fe7ba852f6944daf39acd2bbfa3909f02819f3865c651f4b9e1904636868f1a0aae0a05c8d376d809d8d63d03705bed27497cc83fded6886868f7adde811a0b0d027ceea5186c07ce3d43a523a5d207495414d8a0aeecbef892689ca9d1c2aa00ff3b553516ff3a72a3379fa7fc4a1bbadf1ea0f664782d656c590a0bfca8640641371c0f94184fcd8675ab93c31df18f3b7c40a78c0bbfa5b3f2c4e9eabf9c00a17371e2387df0bbe9189b9eaef6418c12db41d14cf83e4dd0d3d95b7e6d450275705e9c2ebbc0c5969506bf390be7c7889f341e28caba054df5d76402618d01f256627f58e792e45caca4b32587ce2a652ee68601fb71e6b89fe179e77b6009a96e72a03c1996481eb993bdb4b7d73ad8022c170c7d78f20de23f0be11415079f0d54979e319a5cc04c3898342f4417f544604fd8dfa836110b205ba882a50f8ffb927a36f6fe57de160907f917c4dbde5e90c4c42e93fd76dd4485b1592909643abd2d1a3dbe1ede9f59afba10832abec98b0ed5b411b01c1b5c6d1b144608742d01e5ab51f2ab21148747dd30ae07bc3e1a3b4e9d2bdfef8abc4cb7ead009188a4b33b4b5948171d21832ada8cc8e0348c0cfd870c7daef6dcd79ff74610aa576d479b461fdf85dc636aa5722a6598564e8ac6439ac64561330c23907fb028093788bf15fcbdf449eaa7b9380fff2cebcc5a6acc23cec48a3419591f4c20386a9de89ec5d8fc18516bee4727688f8445ededa7caebaa0fbdf8935f1b2fe0813ac7e788f97a8e872193d4db4ec19ced75f74cf498cf87d00cb1a12140c9500489b19a4ec27a8dc3a8fc041a9cf59c430ae994d9ee730f4ecdd43959baf8e0a70023cc5de426904c2e0b3d0c58674e2736801a7a344623ce303e6e8042ded06e6092b240b9189e946e1d28aa19c02e31aa3f70236c83937bbbac9204443560c9037f598eeead125d3476862a6619b292de95db1677332b991b996f36f62f50b789f57cd38065c25afc9534769c0069ee4d8424decbde837b5f5563781113602bfb45f80fc8f501dfbbdd24d2448cbfafbed3ee9ecd04aa08b6299620177ca0c47c2529b89fb8ed6add5704e62acf2974a80511f94687850781a710b9bd2710a226b4bdd8fa87bb0e0ab67dfa73af1de7897dd47b0ea603d47430ac1ec8b7107b68e3f584f2756c3e6799d27c57261083602bc05573b785f7e072875168d6f00ea229fab5185839564c0444494a9f7e6ac5957a16842b509d6ae6d7753f0e50be3bcede698ebad9fadfc276877b2b021df919b7964d1d32755ace2499a850c0d22e5a6c18a21285788aeae7afce64c9734e50349fbc5e9b9dc796d3c7536e506d25ac6cbf2363b47d2ee00da877d1b207e97d5af25523989115049deb50e930bec1d38ee9cdbfe60c04af9489cdc1550e5d033a271e8c894f6b70b454aa36a0c91289608d3d8e324effce230c2fa11e5f74fcbbdf38530842cd75b5404558c07aa475d9079c3f5c2fa44b87b4eea63c148debcb13a2f1d786b046b4925d32b050e949393e904568dd0e7f97cb301ef497ac0f8b999a5525c8d0b806c33d2b90bf9412efd6187258bf2a3eae76f8ca5d477c0ce5ce3c71602e3a7501ab9276600feee187199cf3b5de125e3b067394ec3530f55b607440d49a7ea628ae6409b0845333cab6ab13e07e51b5a388703614768399424c919166836dfc611f8ed4d0e4741897df208fbe4049e8674688d8dfef4762e12f4c41ea3a337e1f05131b30c16f34a03d4a5faa882edef2ecfdfc6b184b8af12a4182abf4edbf9e6b371bc0130c3fa9ac3690af65faf5d78c39ac176f59c81d4ee2d2e87c6071c8167731c0d65a38041af41f92ed4399e4175fd4eaa3ddb7b53d8ee1bffdb279fd1d59e1d0f07ec6e63c4ebe0fa6b0ce28ff285ef00872b869d19e00dbdda52b49f1b655804eec84c738de9cc7b7967ef76fc1e5ce1aac617d6df94f435de04b71a80293b02233d574c4ab2cd0621a0ba200e992227e274c1e983c0910fe9a919b5fce0b103e96fe5abeaa04e2ecdd9d1cd2cc5ec8a47422c7beadfd6a951cfb27cee4cf106c88236dcb5cf2c885a6444fc350f0afd54897e7f93af7591bc2a19f45dd6970a399aeb18232f894ab8bc61478d3c2798bcc49acd005e7078c6cc63cf8a42fc0f57d987dcc42669db18dcd0497ce5d9b4f2687c4077a31140dd70c439f1edce0cd812dbeafc96feffc36d0587181338849e7f9e57ae58adfebef9049e7a1d7904a847aac69f33db9744cf311a1f487dafc02a4526784135b2213fcd861341b2063d88a6aa4a6e14f454808042a16aa014785a781f74a4d641e8948ef5387b0b014fada6b9ab71fb5319747e97e0440713a5bb2fc703a3ec892cf8cdb33c057303971e2a412fce8eadf5f037cacb3d3dfaa9bda984b16f41dd8235d542acb1a3025e3c1e3ef8f0aea805b29ef5cbcd1f770cdeb7627140fb75676aaa3192c11d037f50e781e00f4c9faf31b352970b2feed1c33218bdc47095ec97a203085cb80fbf1cb2fc07e0842dd4ead52cdceeb1de47d8136446b88662542451cf8a4d860132da06bc94770efb7d9033033fabf3e276dfb92434d04147ba65135c4596bd09820326a8ef4729d1561116ae65dca523e1302234e863376b2ecb757940427d04b861f17163728bcbde116506368b3b56f09bd85fdd6f51eea7da5dbe3d20f009898e61b623de90e167acddd298a13594bf151d345206d8979ee361ce3c91280de4cdcc2a4178acf5cb80f884499c068ea1a89f2f5bdb17ffc712f685d711250e0dd7b2d876ca3d0d56055b6c4bc40a1ecfa93b2a6e855ae17c2b2fda1a287407cb0bd0948d0116300a537ac1c7d9517ba02fbff59c3c631c59f54da697b9d70db01e50a0a5d0182d03cc5d0d77b63252553aafb5617a481690cff1e417c693091f55384cb1e719f7e2928433e735f80abea3c6806e6738ef2af7228f48037c07286bf4378b9ab9bdacf4c1bf055c2fe04aaa801b55b0b3e8fe12fbdedc0aea0e5e29f36fb16fee5f0640393af5b45420ccf4ab693e2555d88e278d735bd2370c2310cc8ec14aa57e3e5f2c86e0bd56a6f7f57c96ecb8a1a391071e8c9f36a600d86fd1cd6d9fb4a1dff363ab5acff05521a7f70f11d2e576980c0a93f4bcfd0f48c3f8db360325a18e391a5610daf26218a401a6731c6fb9baacd26aab3efb09287d6af54aa10ae3361f020858de66efea782d3de344538084f4856e02161d0c11f95a7df3a3227a4131da51be26097ba51679f334b0ee8938f988af9be4d806713ec9dc11678df0cb8deac6578855e95ff25862330bf38d5f166ed738069906b11cc52928aa9f4a1721aed3096d16d52a4b1a019a67f0c21e054d0fd8560b077120c683efe01a1c4982841c8c27c3b8b2295317e770efd58c5c751382901708b084aafb963ee3d2eea47d90a38f6becdd3c6d5f52b9d5a59be027f3ccc0aa078296df944f2035343c086c61c72ff889679cd699e2368a07c7053e16965abe07cd64043856fc94663aee6325a54def2a1404c7fb2349986d57d30df25d32ea020800b63a436b12f46de1fe7f628bee83bf804d0e7e08563e8dfe0d3fac6adc0c06d1fd9f474e47925ee6edc15a4abf7461ac863a545caaaaa27fb357a1bb6b0a611adcf728fe02b1e85f932abd7a18582dc96d57b6e6651be7c90067cfcc540a4d0d0dd6b053530640c901cc812c7618532cf65bd93e6870a643f2d838a0140b6d323533175de3f8ad5fe5e7bea282172b613b1e452b9d59ab0ebe49f6e76405f5c1018de4f18ae64cf0e6f7132e91e76f809d4b80c25eb9e113a77135cf1a00e22dedc495ed82fbdb48dfdc5b345fefd765da95c3320570c0c34031b771a501e9b9105e78d3e8a19e3eb27032b26ac18faa474bd95bff7aac372047df802507da397fa56d4fa921c536dfef8fbe5d4f45dbc9a49771249086b638a63f723f00b6c99fbef8cc066858b07a5a2c5f8c521fb76e608d42f5bec36b9201af1f4a0acb764a8f608c3dcaa26883824754c97535e959927c5934ae0a690ff1619d78007fe66aad7529b2fe0eb855d66bf511a501800616200ebf192a0743ad5c6d3b0a157b4e27188cdd9712b651ba97edc94fa6ad70914a589bc2683fc9f488af820d5de1e33709d209d523784da440f357d92135393fd1cc32a2af2eafe1f6a7a0044f87dc2b0efc2b5193315e68eb81d597af702191a9b49c758c1738775d7de20f907137c13d91d10d6088dee91a1cbe2dba5932bcb30ae8d3827a06e8463b4207b5646811c0fdb3a8b20b02af28d28a18d5b12fc711cffcf50ac6e254fb0f7d009359200de982e31be3f766835b3cf43dc4c47b4f0be6bbc64294a4ee418ee40de1f98448e04cf13cf193bbbe45c21526ea6849ed3a78531489364bf4f8bafc0a70256f8632008bf7f409375755b819ba9a4c5833b10f1b01bfc817387050340c2b7fa941456d74f032c29c2688b9bab90dffbe5ebd33f889110f2a74168695076b07e00607b910ece4ad97691f0e6a4b3b8b6adff0fbdb14bc467daccb3bf90d34f6d9f47acfaa99a28fcaddfdf587bbe3be268c3b47f281e3fbb6b86402a20309dcbb669ab6b939dbc7e02d57f8c0f5015261aff3517ade2978ac8853d10a0a91769a5cc20c64086a6dead4dccb330e3a5a94e7bc55156ba4f7aaa6ec3c0b037688f7256b664d335d86d13968a62b8821fb0033d68c98c41e29a08f608dd40d2b35c7858fc8c9e91e356af041aa91261fb774982e9802b076534bf6885d8e0aa6135c2c7b46107941595580eaa2f4c9ee9789b9395b3331a5158df4d714ec0aa456ba7c27318e798a8c75385d6a2aa1deac41543cc3cab0f55aa81dbddb130cded1e0bb5528feaf4dddbb8f08dba9ba5a374d779b03e5f6f526c3d6bc95c608ec938d2bb12cc267ecc9c7df88652ae8dbeacbf4468f2c31f52dda5ff0b44b04390cb5e4516c08c9751a2af38c66f3c28574231157f7080d20256147d478e90caeb23d226eb2f94f5ccb4d13f1efd01a5bc81c6db1fd5b52f6a65d9878f5c4088a385e71b812137611cc8208e977bbd263f74e37172d27db3ef1531145af34064c3efaf01a3e5cfaa3886a4fa1ab744c5c914d641ebd12a522f1a1bef4b73a0cdeb131af234e6b829a1f4b6f3ac4605cf6763267fb052b4c9e45bee229b4780ace00a3691375ba578186068d28b9ecb884d13820b14bdc5a4a0357bd4df0ba09ee43f09383e50e0291b955e2fa5dc0f5368c686c6dd0a02c084ad03603bbd3090a26264238d46d7ae650f6aeb355c7cc8dd567c05bfef798e0696ea2d308790c375813154c0a01371b922d5184bb982b51141ec65832abc640a45ea035751b03d90df73fcd1feaf5a24fd448cabbe7fe5a3cf412794e57201190dcdaa1a6950f820707d9b77cbe9194b07a81bed7b5488b252a5072e524ffbb4b6d88a333fd058d25524fbd5db55c844aa667fe9103d1fa00358db2c7eaff721fa0ee6718f80ccc64bad3252aa71c5bab323abb013dd00527549cbdc7c88ef4ee7a61be728d0eb8b97d42ec7fc98a8417f5908a4b6d1b46123dd8025bcbb795f948860eb92e488030014c794d9fedbcb37af2ff367fda24adb55f87f4005a261e3fe1624c23d48c762fcb522f6ed3325ee35500397ca34ceebb9b4055f97e0fe22c26f08ff12ad1c842501c06efb9c1fdcf93010ab03d57e91b62f401fdfae5806a3d5d3ca871b4df6d76ae7cb7d7930dc106470285ae70f4f82df5bd490a3a57912a25fc41e2921e33eecb863fc31a3bb2bf3ae344b221b097dfa1128765e1cdd69a2222278099435cf8a7025df6feccd18eb77f7757d54119a297c60d4232e2c4fc79094b2a35b7ef3b49bb8c7daf3973de566014f9a133cfd042663f6694f18e59a6d02b53b7ba375e0ae7137693c95b9cb31c78aaa3395ba2218014b3a7d7384e364506e61ce18b4746a24ce32fa833eee9dad6409ed1f759e0c321e59030d41be00f56b8788da170a6002bbef81efa5a0f4cc05a06fe57de0237147f317c1f8cf6d0a9eb7c7d785418bbbc890208071c30feff7086300383f9c2a5bd6ed02945c59f7f09bca1281ad81fcf4115ad15558af9a923a0a765401b8b405b37a7f9e1b432d15d2cb45ac554ed655c61f738b04d9fc6590abed04e46df6ccf3cd39c70d3c86bec2e04026d73bbb34c8bf6ba506ec04f7ff7813f60dde267a9f51ef2cd73429b0ab9bef6bdd259ca3a0875037e887322fe3547adf1f9c7ec9c785d7461c0dbf5e6e3e0e56242b741adde8e855c67c69beba61bb6d703d8d9b804e082336e473270fcdbc0b328881b61ab0a9be64c0f92fc2e2d6ed4b52694aee4d39e0b43ae74de1429b0ca679b7244973025baaf566941c13218915501b55a5096ab2732751a333188d4aab6e7bd1935e5426e968b94d61762457f3a469cd05053c381047cf6904ce6beca7e3e0f8bf1a1873991fbe89c5e9ef03765ada136118eba5a95c8e5558d783e8013dc8510f1c5ba0103aacc8262e2f164ab7acb88346067f6731947524666ff54c726abb146427db2eebd658878823be7ccada77a3b602b9691a3b023831b708148bdb94351219e76d64eefcedc0fbaec63b1291ff1d4cd6400035b8ccbfd676553266dd8a2c1f38774441273bc8cbe387e15366ce2486985ed2015c278218606ac8dbfa2597947a97acda8d8c340bbee06adb6c79ceca8d807489450d6db033e9c434838e5ba80d420a5167466b3d6a0d2f74c9034f36fd28204c0379e91d98c894859988a81ad3ed312f969e13936cc267e834585a972298e6bd48345da633231f45f1fccfd8d9abaf13479d432a0b6998878dd26308d86ba262f5b4ea507bf3e85cc6cf708c546d21cea21f90f4542801ac004b406a09199ae9a58299be3b662cc21206270978db5d201373d0c074607bd63dfac803e07f624d7b19186a2ccf3a1d39660a0d02375cc90c655f17a82688eab73f0dc04782b797e3b87a6f8ae5eafc5fc69bfda69f8602f1c1fbc10d4793998b6fdb1fcac8d5a4596c8fe39a6aa0d9ee11a944bf01e825535226110aed52d8d2b224304fdc2e8283355e8425a978f66e7f124c36f564d5c8e3101ff59ceee19f6052206cb6cdb33eb52157eec39e0baaf887629f8853d255757d4c3367388430f641c9ca2aafb35edd847ccc7a967439465a9f71050cb4310f87677823f432ef54cfc0610988a9d2460454d6d21357d05f6e74239ae0573e80785a76d593ba630007d8982701867f24ae917411e650978f6c0eb49f2de65e510167143de48a132430fd757375eb7e713f716864770d8bb0fdb1326c8839ba45bcd1efbc346f879af028174f84ea70dbef097137282086e872d33eceacfae92254531f1ae9143b3b6c646dee88e67d700b5e6c82ce8fdb740665d77983fd664153df40461be914122c0398925e57f19d6b2c1d41193b3318fac1458c2d856ee8ffb0b920b4e4c239bdfd7b250862dec014da90b6985cb711e2a039d77848b8e140bbd30c22cb717460f223ed6ac84b97c3d99eb2a5bad7658aeb35a70080a7c91b95f0b649ce6f69d0e031bbc5ae455736aa3d3b35fdf41c2db1cf3646d70db380e4428ff97be064f2d1eb8d76cb4d7ecab5a7da0881d42a9df5650827f21d09e80ab18203a0bcc8ee72b97a3baa9b582748769374e538f24344c0b27a627c046ccbbb4d4d65e5607089dccd77595f1f190f3dadf930a78f6fc85fcb6a823561fea66394b2fce62610f0197664cc6a4e81180342df347d36ad48460c9c16a75fba95d0f45773e87d514da9c635cda60c746260af1c099796513e3317248ddf69ef69e23025083ce8d5fde1231cec7c41f5b03c76fce4a0a05d570ce013da78b8b6cd2345ac1d17748392b900158e094155a6787a33a4d6bc33c4ed2a72afa8038a4d050324a22c924b1fb5243e121dd7f6383180fcfea0fb3e55288efcae3e07c56cbeb1182bf2a63ba53897295ab300df3ac0272d666f9cdab504a27b71ea0aa216ea31c10b349b2659691e570dcbf2abb645f78a7913d5e1ae3b1742d1c1ce9575ee8c232bdb1fa48ea9e6ae409dca660d988727de5118d8099ef2731b92dcb2b2b5182d4756787a6477f740c0b95ee9a8a956c10911c94bbf82a3cafdd10843a84715ca1c110c0cf762117ed38453729578dc9fb71f1f49418c0c00f23c85ead07e44f7d695b1d16cb7a2b6c8213995080f649fb382314621c27247cb692c17f9ce1c4ee3c4c3c01910707aa95747866039117b542fcfb12b3420a89e7969f0a5dbfd0fa01109150364f25e2244c78a12a3fb6f90eaae7ba897c04017fc9ceafd9293472b6dd456a5fce7da72d38e54921b7499dfb26c85f9168468153e5f3b134587ff7e46e42d049fed1c6796c9774887c523b12f3801a951c39df452d7e38c39077ea8e9b03428d5c54e79f4284a5e4771dfe419bae9cd8238c3c5dd588ab57ee106eda5f39327f07f2f5bf0222f30164f7e7860dbafef914e7b6377d81c01f8f49fa7dfdedd19d19f0077803c43fe9631aaf10ccd8634c46bde6382b89411c906e7321cb8503028b98400909f1917df0353a7a40924ef038e528aac9cd330cc3663dda05d12cdd00306d350545ac159fa68526693e721c6a14a512a8684edd2bd3d2a03c4a8e282cc2d1ac00bf733528e0230f6817d158860d5812a7276b44f75ad2915215d478972a705e09d4ff6a5e367576bafc1c788d1d8d38e714b82fb0f8195b839acd6aeff96d6e0c9b35d0238ccf229db02d85c6689c03715ac0db13defb1c456599feecc0a9b70e09142e43ee959f32ae31049ab04a316403c214e4381bc08696dcede100f59f0cc1aeba14149b269d4e5476e3a8566545401bf0985421e5f55b9bc18911f69f0a8036fd364dbe6eff2d68e8c403a9916aa730fb1012fba88a975851e386c78109b8744d833db1cbb5468dae1914c1274619fec797e040008a931e534bb694820ecee77ec683e3cea3064a8eb436a3fc1e958f270c010d8a54504c23185b478c019f6db80ed99f2abc240e05710b88381fd2ee696bce8fc80aa91ce1ca17ca0f04", 30 | }, 31 | { 32 | name: "3 more inputs from 1302238", 33 | txHex: "020002020003dd8b1cb8d307e1870b101168bda2047709a950de3473adc8775883e9442beb6dfa0cca023d82ea8958020003c4f91ee836deba0f41b669d5f9c1d6211dcd816be9ac2c4b9df4711aaae9f1b762b5ea45b59e771203000264d3ed9b6ca4a0ddb4f1eca8360d37dbbb9da7c807caa327037799538c9d6cc50002eeab751583cfff1b387d4422f3466a0350fa22da8abc1d025b9502713e1094070002369cc87c270e3a7be9aaaf611f28bba88983450786524370ce655da8c301ac2b2101f0a05b68101fddb37f87ded0b89ded89918b2ae750e87db3b0f84614e531898e0280aef0815ca07f3bb05444b33096dcfdd08cdd88c2a8e20f358fb1b350d34d6ea2b034c8d6b2c966f5b68528349eac7914cad1d063f2b35c143ebcc8c60edeeb37a070ce44dab824435e5188996505b0af5c55a7d81ea069dbf4a238499b454dd2c457980ca561025c89aeb1d2bfc14b7a163cde9faa777de9e61a77c65d5e3005fdfc3a0500baff5c45858c1f94b8f78d3ba7fb3371a7c18010bae796732cb2133795880260b2372f3ce5db8c35f536d5329651fc0b6417ef2c7b2e440a21bbeaf4628f04946494dcfb900b1f675b57d3cc085819a1080096b67854526366e3071284070378225b0bef1f1336e84d1fb649674d00817ebdb4ca24ddcc94a2e4ca9b242b063e49928375b585459bd7b5ae3f70b8019287898deb384abe5f2ff11bb98ceb4633fbe66b9f592ae59cbcf8e9a0874eab49988179a53fdd01088bd885adad4c0a8b041ebc08e7dbe566697630da9e3b25d5360ff5c05c9a65f782e271bcc2eb1a5478fa4a556ef6fcb18d45a120d985ee8232eae49fbe9605367e7f86d833ab033eaa7ef596ecfea6cf0aa74b0e702c4ebcff3093fd6b6b50efb126e8b1845500522fb5e53f277099df162f9195b40d1bf52e99d1127dcac7db5067caa427f80ea13abfcbcaf550fa3c694905de1bbef0b1ba4eb5e0c91ceb86c53897a8de6307cae488f6031c44f693040cb2da6497270c51648b17fac48d9d081df400ef0809907f2b40f1430f7a536d6a74aa0a1cf19e6b08ea793f22931357737882a7e009573db2abb4f57fa455dab91018111081b3dc301658f7ef8b870c4b27e9a79307b5e8df7f19337fd57332b50515bd1cc56314bf290f05576d661398e0adc39007416457ebf428b954d643b19badf2bdd95dc27fb3c388db0bb80dd1f03507cf01876435e4118034b055c003960590b305bd606edfd3baf74b0045f779797e95092e4e6f1f462767c1f3fb7db455827b79ecfe6f5d35e95b245b794d6344339b0c8ee626ae4844705cf413d7585fa8ed4dd1a99746f95babf929f36679893dbe0f014fd7a8269f8a2ac69dec0f12847708ec570c4704ade002a119f2938631190da71d19dfd0f62157cebf8311bd0ab75917c9a39ffa8f414d2c662cb6587f5705cc77f299f8b7a9e40fd319c8555958a85262df1cc683d5d7f4549f188a509b02ceddee25522091cb723843fb518e1d3a437903f287bb41dbf1254e781522e20d48590ae99f88e28bd950dd36a1c42fcd147772a2214530487bccf2d2cf019d078244f874684a990f8df577fac9643084a7215a791990f809f75c3efd061c3d086dd695a23e91cba52a2201b562da546861e330df6c331fba46eabdf60a1a5005ce6a99490b87742ffac169f269db8b7b33e2fd0240c54b42799e1bfb8473e30424d0cd328e848bbc94d4fbbea542d5796376dfbbb866018f6ad5e0139e8659019e04c61c964a1eaedb94d4c90f2e0f4207bf1ae83042124a83ede75a8f67f40e3ac4dedab5c6d5dfa78e359436df0ab50c5d258b7af45ac151fd632782e02a078ebced1a088e7ab8cb955cf4083a595464db34b36e6f5d18572b2ba9f514c50e6265424d8e30712c0cf80e188163c1949799a62591cb82e4f2b6e7cc37aa3b01bf614c57fc2b512dba2dcf8bdc1869e62bcaab41058b301b8197ab0b4f99fd007ef4adb9721509ea2d4e9aa49a705501c6109a51300c3acda80d699ef8963506c985aba70207fd7cdcc01d5de3a14a5d9287e1277418594aa7da4ca28e07d20d09e283d87c687f2e9da46ffdb40da2e7a77644cda90bfcb4bf6381988255d202a05b062951c1eb417af4e55a1bdccb7e5f166e900eb1d6123d11e5645799a60facc8c6cd32ad30b9604cffd2b47152a813e534fb963d12ae6c835df35b84cf0da6f25d3fe5edb29ad7340c42b39687cee805aac01e419e37f795af1c5cc7b30bd3a729258d6a37dec95a6cae96dfcf9c6ffa53c3a1e96be5c1cf9454afb8270330d6b86dc98555c8ccbd77141ea54cfe14eaed9ea1437b00205432d74f2dbf0adb1a34839f03f1da2472750ab836fae40e257ec8970c90aa1a833a48d5da510e642b5370cdd49bb9274a56fb107c9f11f1657b4bee58777a6075a1c96abee807fb92f8cfb5498fffb1d4ec4fa2e4f78788f11c4ca1bb573687b9160b2b0cba015a3a30e363c2638b27aeaae44971cc5c045c7238cb3f608e02fce412168b7803533e324b2237cd7211a3cba52bd18533ece205c653665111b6f6b8a862eca104513c8221443803b4a2d4f30326ad724c77363681ffd05b794a3403ea3d886b0348fc019051ac715d1ce6981d51c4fd52d56f4675b6bb97c19bfdb16a45182303775073aa18c45bf5d643f9813514960a05c8bd61d09424372b80a9a42a29730def7c31253ee9a67b16ef798cd8037dd471e9820b821eb536f2ab7dcde42e8e0838726bac36dfd4a91bdf0b11a138cac29eaab7e199d0ffc16d11d83ab48ae609eee186f7ac4e7d5bb76f40f3467925e01a3ed7bfe4af13bae77745342e7bda087779adf1e9dae19d7c36716318130ca0d60802d3c547b2c9e77a66a20d09fa09ab74a81c2769a169116ee549a9b7d369ca3f0ad28da9fca5f7084655cf398c01e392793c23676f215840b057e0308cac2e4dbf31991d75b79b0aecfe8c9a1f0bc63729a95d2228d47124f964a7f084b5f401cc670cbb7244089e0f4fc6e9e106a29257139e10db623a3bb9692261c86f61b4e47ceee21fa090381d2330ff8b0f8d1d277141ef9970d048a25ac34145f5cf90a8d8ad5ff61956094f537c1d710da5aea831c28a5ece3407b4b2850953f5be27569707827015f861f5ceab191c00dd5943cef93fc95364f085ab6e06bb0db7a155514c432013de8ba53054561f06ee441ff3d9dcd0d656fac2ab76a0203cac9c3754a0f4c90c3198c33238172903c6805bc4d73269de45179b0cb5df0ae0c3bd1fa9bb8fac661521ffe983488304c60a8b80fe4b1bcc41b48ae092f3284fdad987ebf6dfe62f0eb13150e920e2058c77f07bb0fc0ece3fde74368f9aa203da6b13d7dcc17fb9b9744aeb8a664e0aeaea9b0f7bff39a43d4dd49c7430c792c68b84f0af490c1ad4d576fc1192470cd6648fd9215cdc9e4699422ad92235f2ddfe0ab6e8d54540d42664629bb6c308c80bcf8c329f1b9ac1789b02b410d2a374080fb55335ed7f7164c135399a44006a1c0eda03109b526a5398dee58fe141213a808464e8300a479bc4ad61fc080afc8270a42433e36e2a64087cfab2aa36d092f91fc2ffaca540da77a11a284204a7ab76dd7bb5bab670e360ec5e5ae2080b04700f6f3d3bb4f6c2b57467acfc08cc39afcab1ce8d2347df737a76c56496544d1a3ef875e2777d345f7f258357030e0bceeef090906d224b641bf3bc7d141f03ab242afbe6553e1a6f8e9579c80c516669f452f2d3d75a970b83d5e1f44e53651ee4703ef1a0db71804d4898750ea125b5b16f99004edc514a0339fa6a78cd58202268e2e6ee609e0f5ee5774508bce58b2c7aadff7f4250e7ba7283234f396d25a168097dcb6560e0f229943606abefa246c73eca6b51a2c24446e2c10fb2b226a9f0559ac4791b7744cad42c026cf57b8642facdaebf21027d7be3c1f8e158ad8523a1d4e2fdd8fd1f0877930d8f8abd1bcb0422671218bfd2340635b79cea3901f1c4f3e2c5900b6368d4280179d65305a7b8dabd72bc9cee1b8f4e1c788b8e9f0db8257d496412991b4687027a6759bfb2aab5729b3fef0a4e3a5bf1b447ff81cfed64f296492ee26ef1f90b75b825dcd208670bf8da5b6665281ded0d295f08ed61f86351349423be3a4b06737a8f187a923beb7db6af3db48ef2f98a77317b6d8fd8b5888a969e4ea38a0f17509cf0614e57238d1b4acf494f6ae31db34b2b19fbf353f6be785bc5fd5b07fe89b048b6ae974177aad4183c2735a9dccfafc400d7ded8ebf34415bd495f07054811aadad177c7a39646c65454d2b2a38c58c1ff560e5f9563e8a11dfacb0b99c8494f0116457edbf15233b14512eb2792d82f8e0f8cb6a48cf02b28de840ca66b4968f98d48c44bacef159279a4288455c2c008c68dc1bff225d8a9fbbe0471adff932eb0e327035dad675685ff3b0fa83bfe9d8cdf115c421e0558922500347aaed5248b89360405021a0d554ade578f84d1a2d4c47165ad17abd173cd06a97e9967fe48bd74a464e0b282c9f15d92ce41b200c8abc2c5089b8a1f89ed0a2eb3fb80837f7700a1c1096c880932ce98b6d2348a8180f10cd629ea76b9f20d29514f339c9e44e55242c0eddcfcf8aa65e943482cc6e88fa944b9739315550f1d2b6ad142ce703dd4888e47c8004988b8928e3af39b3e192c34e1db043f8f0d97e1b5f1b3867ff9691bf4ddca918ae178c99e1375d63b53fe453a3a0e234d0205c3a161cfee05d12323313f135ef9d3e8eeedd4d960140e66ebb62fbfb8ef0cbcde92b4c1658b395c125a7a4fe6dc8ea5a8d23dadbf45bf1bb9be0199961a0ed80b4e99056cd3669db053c28d8f959e69a2b66b81caed1eee415b37999394006d2dc7eb764bb4fcb78781c12021bcc33de9afe86c30a91308ac0a9d7875740280d2e05f20715bc740383913a8408655f72db77d002b9aa11a473e247efb470ade8adeffe0f90a74d0466db6990f196f2031a7a372b7dd6cff633f186dba0506684cdbcb87ac52f3f4d0733fe18ba1713d6528b04c821d4ebdebf233fa4a8f0ed6447affd686090c6f90a41ed058b4720e09be8c0994ed6a349adeb189aa7008a39d2b01e3c4ed5d5f8b03c270a262013bd7d0bc4f1b4e6584dc47540f7d2c013bcc85151a900527b7c69f8d258887bacc804486ea788b8b9c0a5656a85d650907aac9f5b538802adf612d04267a8d540fab9fd47da424863823c35a492bc000a31ddb2abb2aee695e2cbebf257dbb94f55da92c35d0f9fd1158036fb9b3b50838e5d278649b2d536645272cff766e183eebeccabe42b7b15307734146b899063b0d6d3bdb6cda6ca6bba15703b75b331963e7dfd58c045de8a40596c0f76c09f21c1e9c0093376cefc172a68cda82d19242f268cc6c734e69c5084f474ec90140708d55e770e24a8c0229c3a3d1069e3e7cabf3f95f7943e56c4b0df68afc0ea597415a29e099cbf5ad9f91b195ed94e8dc4bed851afb55167f1180b5baac0d03ba68956f7c9e3d9dba35dd3137d0a2a4ce3c15102d10f763aebf18f4aa620a257d0d7dc19447e5a999bcbedb4bd4e5e1851ec0f6650ca220bd6c926be4b005633e89f7238312853317b3738cb517ca8dde996a5388944eb4a3265b6687240f8f0a7bf9473470936ae8344c1cf41c7465f7212bd29538f42521fdd47f548e0aac6bbdf260e975024ac011a7e8d85e508645d9766a4996f8c2c4af1724f9b105b76163deea852bd61376cb2ebe668465d2cbb83f5bc20cab0fc333f108919f06bf1d4974865ee58d4c4a2411049729b720f36e1ce13ce50491d2ee920546610fdc14f47d65a1679d7fff74199c3e6f193480946d396a7a62225ed3c1c8b3e40373d2c5eeda5de98a8e50966cacc44b236c49a20d974748558b52c0451d44dc01f675b23527505fb6adb477f51b0956f3413a4a2ae5772d39b801b955e865710bbfff6fd48cb246980ca6bce1cab522a291b67e148d74332f06835114c1829f07bcb5e2bdf9e44992cd8d99434ce96ed0058b7801364d1940bf36c482b0fbb6099a71a388b429da22fa8e29ba52d6e9fa56d856aa519671c702cd37d4450e210b735d246901d601ee67f6250b6d88d1b894c1e1af9279e5e4b67a8628a53abe07c421be2b8e783a00b977f188f4d6492396ca40d0f3252e63f99613dc4995a50f7e771bb7a2bf0b388d58aefd690ac6a02e9245869521b93e374f5451081a3c062f1ec398fb620613c3488d9d94e5b1333449f1770135a2790ec02c29d113590770aab13564ba7fdd802d532f1d7b61dfab2211d7e8a24621db0b5ee6119d9b044f11918056b2f019d9dc8fd4cdfe1ab9447bca7423e90710aacc3e7e225a160f7db3ef9d54ed4328ec23e9ae02af17676c27ef446b89b738a09dbbbbea71120c00019c1d85015e346ec8ecc86d0556aa81d4167a0bcc899db706fc98bcb2760a9de488e36ebdbf60e3927443c904dff63fb0f50c6ad178a4e031e1b7a854940e05c947d3e92f28326380bd60d6fc9ee2fbc8616f4d2a982f3ad1b4bee9615f06b87069813a83cef8797394037237ab7c083793f7d41673e5202897134961a0012192cbed86cc76b71d852d3e0ece618ea546fc2df5b6622f1ffb8410732b1300646b378e8c2346b1692f3c1444e2c768d4f0f557909f6801c12911dec6bc6cb268309cc821653e3fae655f7520fb4a3548efb210e76197136b0c0c8f36f586daecf71ad01094c8e9ba38131eed48e2c1805392fc3d0609f877444c0618f6f812ae2e00b5efe654d7429c2f2447b2977acdc6f7aa9708ecfee84bf8468ea14f3e8dd902410c06df11cac55cb81cc8838b1ba3a3b922579df328e8084a503dc16f60ba08a2f9823f5b437eaf9a8fa19b4532ac051f9c136a2a845f95b48c77359592ed247ad77b0804b86b34ed4c5fca4364e2feabb11daf0ead0ea7f9af9e3f2a8f46f9eb63b184c759bbd156889daee3053d3fbe73a833a6afa86ad7c49d55a06242679fd9b083d591d73077ff316aabe43616740394b743dff2fe24e751749cad8976eef9da7e2818dc9bba323bdaab2a4d94790102eea38a74f42d53d4cac84a6391d8b6df29721107e4d1d8caf03e8bac7499d29a797df1d9dfa361e70ecb8c8e59ed65d774b86f9b08ddf38567eacda1883121c2c72a3589b5e23cda5c471665f630cf01319acfe383a5111de80b48c8c10ee4c4d513951bbf0733707c4d5400bb76d09d919981b15df489eb3b3a715c0f029e738ee8a522f28f9dd2859885bd8b307d029bbf0d39588cef4392e190f401ae74b5d90dd417795c7425b6fb1aea2d14b498cf1a03c158344aaf68ad652fe980b383c52adf57b5f01be5c50944b21ceb0707ca9fb658716874d8e2f4a1139bea4f3ef79abd39011456dfda0844912122503ad86830d0f52959054deb6e8408bc00ca17f15fba810b21599eabdb4d1e8e5666bf80e14ee0c07f6ff6b0e9bc0e5e44673d2eee43d2b734f1e66b3c75ead20b60719fba2bdaf8d0634addd6b4cef35a05362f93a66d20ac9ffc3bbf0851d2e11127cde52193ed57d410c1fea8be7d699cc2b1b207612e2e8216706ccc5b72f884c1d5ff1248548108aeb0351e24e222b1c8f6192a669f4bef66a54fa264a04ebe3081245e1a28ddbeb3c16d3190cae64275844ea5b4fe6e6c553080c89014dc70bfe48330d51a2142c16e2e415d75d60eea86f58800d5dd6ff62abcc522e46a7ea067b6ca3eeb32802bcaddffc4beda4f5f72f591a863264687884462da9a6a8558b4c96b18b102f9355331d1b2c964d1cb9965c7e64118d236b0656f5642d1e454b43378a52200889d3586cbaa64aff791eb39e613f56d6cef4956f22fc9be3c2d5cf2ec3d11acbab39f356d3dae367938a3b16eb5a2d05630675183521d4833569977e4ec11f23310f9c775afdcdd66bccdbbad4c7dd2eeb1be570e019437ea97f50e3fc084b45d546ec871eea886490a3afe5c90aae208dba6ab561052cfcc46ad8b87f06d73d99942a110b621ff7bd84e0f9729ef190af696d8dfba7ea0e8ba97a0fd3c7c14728dd4f739ae4cf6ff54cce2ffbc6232219c25ea407706ea4817e959fb7fc2985d3a747a3086806edeb655176e6fdba1e7e00776f232912cc7338c99e64ef71286a0ffafd709327229e2a71923b1370122a1d4a1027eb6faf51b9f2543fb75b2c1b8a626917c6086b9f5adb6bd3cd31d51b65f820e002489659b405c5ff63c9995641eeb0812b5a36974b2acf9faa7348fbc316196a72197f895b677e5dda4e02d9bf7df8c1b210f9e4a04f547310166c9ac064b8f40596eaacf8057ebbc70eff1b7ff9d7671fafa69b5777de54482c93d398347f2b07df9170e16725cb0d9ef7ef0b17da39b07fc66aac691b85c6174a5b0fcff51b75929aa5ff57c6b151c8cad15398c2870ac521140ac67a35f8e1f1396a544471660f1f88a3e2967f1a1bb9bc1ab16a4ff615b7ec2ac5a9de2f3947c627beff037e4d88a579dbadbccb5a8bafbe3e40afd429496837c6af6ade80779c7f0cddac7397aaa19e461bc20245c660bb52bde95fe3d55487a65e00036203bd8a5119b9e1aa16d1fbcb2f6ca6f603c6b5237815eea9ddd67c57a066d55efd4b335e5c59bda513b1f2c221d8236c48fd48c1fb7881e43e61a881fe3f727127df0b7e161a8a6513b6a0e4cfb71d7e813aa09eb16a432090cc16f05cf8d2c2e7d42c56b2b969e659abdf00d2d1727a9a625a6512444524d362185947fbd618d6bfa61c593e827c0eb399261ea68080f37898631302d915b5ff4ac8da5791e601c9dd2b4fc26bb6d6843b3d1a69cd00679cfe05d82313ba394cadd1bccb052e2507b4f3b3111105770162d34b1409bca3c671887c8b850063df80a849531bcbb5be7a2fc11186f061535ab0d416029fd229fc8382eca1f4fa6685717f0fc9f25c2caeb85a55d0bca1431dabf2260a7d08da26e0e8b7cd6e5e4b41ee87f7ed2fbe559c03edf9d86d9e5c5c8ca5cb1f46d170643c23015dbaa993b8f57b839e0acb7b52d993600e4bc46497bc3be731cd2899f3ae7252f7bd26baa76fb65026e862de6f92c46760bea9fd490728f79249f09b3f276b4b0b9bfc73ae08e2aae6f585c3a099f40a72f97879f95ce995082866baa9b717a66b53574c8b7be2eba0f474e491812012b1437f3578e71393415cbeb33c1b60b1aa41e97d08717c5d857214ce9b50ed64d663e6064a85c1a69024598e49190c0290e8ba2963a6bc64d204f1c0be13a6cf733d04528e4d8e0aeb5caaaa8171caba385ae81c4ce0d31de57f2e865c8ca3880302fbd464753c825864b4cb164fdf2b571c24989575360873d3732b1dd6f68ae01045e552a5ca975569eb86e7f8d0ebea580a067f1208d99dac743d14b9023930bb9394ef4726f8083a56ab0c3ebf4a891c14195c53b8e48ff24dfcfebe8abf17b5af0bc0ef1b2c0d83a52e62a8c1ece2c9e28686f4cef5f551e0894c374c1e0f637e52b9a521ae58801ab25a2fa53e0c74578ea0f77dad0e36a79d1452a4c41d1d4284b94889eb17e9ebaa623f4608306e82cc2ed657e122218454c0e149404be4db2ba6acda6e009a5b257f2bb4c871ad8f7225319e1c0eeb2539e0800c1a331928215e07518c48229130fe83515d06d2d5f3e74b275b2fbc871880ec804b5dd23531b8037059ad755b0fbe7017499a55a2b1e4d18c2d41262eabb0202e0b1a152414aa04c12c383d888cbc6bf9c2cdf69bfac0add50c88d1206390aa991586d265d4da94c37526bfa29bf69e990b899264b5f7851b7090782a1e5037ea5390c40725431225613409c5b69cf1dbe6496bf1b6c310d31cc8b7900390422b65aa0a143285bc8caedccbc93030ab8ec794723b0be0b46822ca62a9fba073ce7fb6c5dfcbfea5e7e6b0da9724126aad2f99818ac65f8aa04586acfb5510f7589bd66846930af1751f149ac2cb5a9e53f0ef0de97449e9a628fc296a9410d06a72e8365fb514122a0a94a554b147796d4112286ffe73caec6575d8347b90596f9ac200b14b6a706918da1b65d64669a146a55bdd52b39e2091e4ffef5a10d3e1a36f872ccff10317f32470642ff57b8a7f1a48d60de624395bb11c9145302d1a7abfafbed65cbbaee48f17fca31c64c4a84d77afdc6ab5123460d3ef4750cc6cf4b95a2ea9d6bb3776b69e3ef55e7d03cbf74f6dc7b3baf8222a86652100e8a7a3aebe349e5bfa66edcf7eadd074e84dec11f1faa5b94967bdbf5cfe4bb03231067436a009c0b46c881024293cc92ed670986c9852e4af93e0181913c8600cf216dafeec3d809cd5c6971320c1a02c00cbcb43b806e3bc215fb9f705d3304220153cb7b012065352c9e2e4477366b80e8d588f018c403c7944dc4e4229003052f6ffc5444d3ab67097b6b8635fbd37d21997a4509034a4f7884d00b3bfd0d54b677f4e5e040cc16a39bbc79edfcfdf27c66dd333d7ded3ab375e3c761db0f6a2958d505c0ee7c304d778c877ad6405455907ac50a2a7246ad03e77c05330cd86db89ac602db1d65af1976bda837ad268b6f10687a7feeff25237bdc1c8d0804957aceed4e684a64831a24c088c677406ac9e03aabe42fc28536f479d98c0b42fe76cd307032c668fc831df01f1e80603d3e24abfae16f503543defd4fbb0cd96f679a46ff775c55b23a30979a7880fbb79003da7ed2ffaa835b66a5ae440be95c97663e5ca8582af41a3e4b05a367a62c1190f5be4b60ef36ea5dfc97b106d286f893246a04e15bbf7be7a6c9c3b5a0c387f05f6feec153b0607b7741a70f19613f0a94a2141feb1c7ab8ce2de246714fad10824596b7e820aeb275c5530778f7d362969d462419ea1aed8c290c704aebb689e9de8416b2e2f443270b4f0d83f289f5c04aced308acb0030fc59da04fc198ef7ccecc2f2ace6af0cbc2ea0c269805f7220b5de9ff50ea1a31730122a4f7f1581eff6cf6674bad5cb7ca410628faa1b6a4be4a1ca907612b5ac370a32a834b54ca1f3c07828f6428c1e143028fc1c0cc8df2e5e7dc43794802ccd9c0abeef7fd1c961c0289421451d15eb10c14fdff48f4147120e801587d9159b1bb4f6fd28f396eabf38cdc9b86f643360a7825bf7a4cee8acc773f14711fb1393fb53698087f00bb309f331164ece9eb0c30303abc5cfa692e9dee4cdb28aeb9234dc477f2b83211f1c3bc7eaf373e560e3b831e49effc3b5cb65faa0f1ab0fba924491b445fbaf08a5a35ec21631a6e037333368e5d94736bf3b599bfc5ac468ff66ce1a2d5339beafb27e9e4961185069177bc83ee64b0499bd1d61e40b483e2e5a76d468e6a5c53a434c5d3f5d74f0b8216987e7e20f7f6324911e8e608abe779912d11d044c217851e28964edb43062a271cd3a01d92f967fd1966d240ff50f38029275bcb250b723a8fd5111efe00f095d419a7f49d54b70912f6b1822241bc2a46e71ee2268c133dcc8c8a8c900c3875caafe2a76dd2cf7f006c72b87301b5d252f93b0ac001c2d5c043ef2941031b56ae091f0d061bba749b3f4b4edb537ee4e9a5734ee5c348579f6a037d910245a98e24502503be8fbd2fe8be587a9befa626a2b5032936b20138c6372837015810d3a8536457da4ca70547ae980a2e55065ad2c61559d3ec7e28de5bd9c501d7fbd6dc77bd056b17e951fd29f472f8572f69dcfa42b8085af84b5c8cfdc609a82bcb9b06e3b46fb290b39a664e9221768bcd77a58f58b825e320b5c628d30b248ffcf98cacd8ad17adde5887c737e6e5418c0c45e5f23fd41d7216b80466050c9f334c09b699c74499afe8653813cebc8dfa2f63a20d7b5df111068e5ae507e1e61c9c13fddad6becce75821895536daf269c41cd7e87027b4a79e7d485108338e6835a914f1c67c1256e8bed332496e081654cbf7b00c772eaaba2e788c0b80bd95ee8367ace4a139e3ae9587a203c2fa0f601525acbbdca046b9d6d95106fe732b5435b6d1f04740316d5eeba28c5add5b04553db925de2ea2cf281edd00073d04e7bc2a978bb0a5d44a40b2ff0f70428a450809ecdc0c8310444c58c508fac53ca83ca25ade64d7886a85d37e59d2d79ee5b5b74ec0117cb8d7c768ef0726308867ba56fafad05ca35925e319b63a8fb2ccb757e565d95f81ed51e3e20ff28dad55e4332fa817bab223bd1fb7160a02c750e3509981077c960fd1a8170d81b384fa0e44fa8db657df0080e3a48d2b9bea93651fb38165b2ac9bed3dd9079349cfc4746a6f39a5a490b0132f4f44d8f9133b930cff4d2a1cc0c6bc8dfb00e23e58613e77a816c875a9d423b543b61544d9101e86eabef952726fe316840badf120a67b4a0c907cf5f1f3711015a453be0eb9b58735dd233fe649a513da0f9555b8ccd80f8ca61566e166d228c7925ff39ac74cb618ac8da19d1a3903170f66797d116024d38393b593be656cd682153976b4268b335cc6ef92b76c313d0e40fb7a9c4915a6d555b1ea101602887fac1e3e028550ef9bd5e1c93ae92ffc0db37264d67be662654187c5279b1c7c50a464982cf653f3c87f4c0d213cf23b097abd43989b4cc4dc03d4a3ec8f7cd0124567cb6896ed5ab8277dc9bb299e280c5e2473c32f82e6663b9d78eebe5b16ed162d98b66831d0a94750fbb989364d08ce44a75745ed7998d75d8c69af4321462cf66f7fd4a2f50bb5b78b04f7ab3a0e4d46f652078a40986c197bc3cd2e9dd8c7ea872527621d5468b7add277677e059fb945f9992fbf0a6c284fca38f2a7712ed5b55ce1b14863c78ef8b0df34920c73250df0cca8793046f13a5615132414646fa6ef3bfe04628ee3236905f0720ad5c7e9d7b9256fc342108554f46fdffeaac10497c6f9f2025cbbfe62fcf27e0ffe6aa8446de100187dd0c26d0b3c93a55688dfa9095a1a8602539817afda490b9a058b1a55b07f72ce23270d1e97526d600a7ef7d838162d4284bff67d96670acc384f930d823d4debd2499db1aecf55408e15cdd6709cfb9df9f50736a8d00b3a26da298100279020a4bff406356dc8ca6bfef6751cccb3ec3f2e51a321bf02692540543ddcc4edea3e4b7068f8e1f5953615f9c81fe8bc80a6fd9f19f0c20fa918b82a0b2e7585e5e2f7d0727d01a3722de01f6df05aae22d72827ae0d07096d76073b8a5ab82defd44671bd56228c2c91050cee5176088738061d19a4ad090c9f86ef57181193a3da279544d5e486de8b99ef881a8a9bf304b89babb4e80803651206c8c416d45fc3b707aff96204e02b428b4f2eb99f7ab7d925fa109e016a2bbcdb6a2d36a7114e267b9d852162c7765dd3376baf1e20d2f6eaa3a1ec03839e246cbe280bd14d5fd7cb956e7a487e0d6a539c02f489aa792dc7d4f768059cd9c042b4e8fc2cc3af2b23ee498ae2b558cf8e9314ed66c72592567a2e93057e0bd6c1705239c00bfb73ecbd38d76e16e4d5e92bddcf89bd5163dd97fc1f0c90efc275aa86be01716b29c3940b2139afa415ca7817865519a9a2378039090c74c98a77e0ec38712cd31ea9998d252195daa33fac0299cb93b5f5a67e999904e936a7c47bcbc1b8f3d161574b01d3f6a245f3a904564c6ae84c4bf681622c04eb0cbcebdf8ef50b537f8c67890a9fc1d1bd7c151fc8c28830e354aa1d9e9d01a61c14ab208966d55530061fa6efa4a12f147d0571e9bff1f5e0ec94e6fa7c0c35c04d66e7fd1bbff9f39d64a71be4a284165d4ff872ea196aca2d9214b38f0ed304dd929ab6fed9efa9bf4685d7b523343fa4cc17c7c6037d7ffad80587590f148ff7601f0fcbbd6f026cd6274b31999e148c40a7870c48022ee47738257e0085b1a3458290280d9d510c654b8c0853cad268f426f3b2b10c400356cd719905e9a9aade7079ac2e1766656c9366c7d9d6ffe461f31effc0e0c89977541bab0f8776ac18f9343573ded199d90daef99837b2af728b03c2a76ddd05b70a6528054cc0f80be22ad92e2e006ea385cd991d006fb863d43cfedc008d216836d58306d385ee9d051f7b8e90b54c0cb376fc6ec70becafa2e3c189b18300cd0019f807e7f0e3c9fcb2c5623ab4e8c7101f1c33c3be9e637b46cfe5f708e10947e5a50eceea83b64cb046a86845637643361ef34504961d8fd6b47f51877e4c9f960703869721d6686f5bb6d84acd485b4538a63ace47204270e13e99d244fe6d537e0f24e83b2ab828f7e429699278824eb8d488a52177960992e85f9ab0854aba190e9bc52ab825ba811ab6bb0e29c7587fce62eb9bd1466289deae7c685a8047a40c595cf00a11eb5031b1791aa01671604a7f33a6e5e6acd30b18aec88ad13347088b884a1bef5fbe21f1014419be33406ac867e2066b659e06ad4b5a03fdd7440677647dda4ce0af3b236fb601326b78896db17438c66ab43d19add0a4bfe14b0422cb0bcf648f3be4737ec0515453fed97f8ef07d07cb95f6f9903bf6ebc6be016794f71250a0434bc1e4d737292fcc1fa23256cd933750cff569ad6ee3724c08b4d984c8c8305f87433df2f5e7a7baa77f2b0ee81899adf45a3150fc7411750c0290ffafb50317e85aa5bbda15cbd5de84804e624384e2527cc4a55cb0049e09ecafce35de98198fa79e300fabcd6c57db42f0a758e8418037a60fc4e6e4bd04da58c944c0118d6e348e041eca7f2d4d02960a913039d1665af8fa39e2c0f204c41fd46e9fa0b41f37f45a78003b636adaab37b1f21837c95b6b133608caed067bc594eb70eb9afc306df09161f1096e46d13a20e558ebc6480731004654ca0f299fb654e863627e9f84157ed44a72cd999931a4ec7eed94a013b299ed92eb02fb21d637ef3abef6a27219f4be4b74a44d66caf321c51ee4966c4a06c1d0830a6ea1de7e2b92d9ccd8ee91cd08f4fdd7ce44800aecca2bd88273f1fbebb9d4092c6ad034a326e5d6027432e1b30f6d631deac837e172820ae5732d007e961505e49ae7132a282b81718bf97fc04a2de9ad30dde28496a8ad026e35b2151f570a56ebbca6388e46d104841f50f2136adb5b979a89d2f2b68e6a1b954efc2b520b27a8e6127fc42d4d79a177eaa4218feaea9ee1c597fdf2d024824a70e8f1e90f22e7c09284afe4ba1c4b42b22fecb49a8f1609bb15b847bf4d33f2b81b995a08156b09a33d90bcabd221bc99ced054f9126bf9ec7fd0a4d0e305cd059f9f9004e98f0df541d8b3689a5b7d2acaf88cdad4345e0e38178af9c60acc0e6c99b40e02458906b64c01ddf5744572c4a0096c56a2ee818abf83c81ebdbfc7cbc6510ce44e99c9b3a26eb48d0c2f3fa6f64088173d98a8da032704cda9974853183e01152735f514cb33c7645e6b55a48853d5f116ac7d6f9b4c9dd616ee7092477a026bb58d19726a9c641f4b8ee3163af2c9feb913b6b9553b35a995dd75d988eb2440803e722b0fe86197567f0bac857040ecd3de651eb24a5673e8bc6b525667b0356c88928d007f2bff70d3431ac04f41f5145fe40131b77b6224cd481c3cddda1acddf3ad79a878a66c2b753f9490807dfe75d7023c2d8b1f23d3ab39857ca34abdfb6e629bcc840270f2ae3a3c019a6baa7d9343a326ba1833db72d21237ab1e206fba4f5f79512e89c3262603df0e21913c8f46ce73b6476cd6a1b874659b68b1c19476b782207879a5a27335a0baf03838bf5f97ebe61cfd4cf143b74feb436a377a4e41ab75a21af84c9595966703c5efd66c1561f615530f32704216be9ca5314ff3f779269ad0a18ea69d11b64431c2610dda97481b5f8ec3e7045c1407bd0043fdc917ade0ce7115037a0dd191a230789e3d24dd9c5647ec7fbce03884eceed600ce98bc6d17754621a92671b52063c4327a2b8f4fcf2fdb0d2093d8d14e3642cc9a866333fb7ead55ba0be2779718026f1ed560624bf6eb1577f54869e190887ff8c709176730796f0d4ef328e7fded3da74663a49346ba1e75aa2366f4047179fec82ccb0224d818b852c9492fb7ba9a6bb9872b667745de7a053cad82baa0762b4323f77fee2dc003165440f414c7ff48f52657ea78dd82429a03463685ec5479bf4ebb73d50c111d9590c5bf3711d4818b59be70d94b982d6c6a2c9e13e3e4180765220313cc4c73f3b3b9c0eb94e07b8f6ba94bb50c3f2c72a6f16497975a8dfb19dd41dc1c03111fae5f460fcda26a0656fe9be16eb5e93ee75b9d5ec8cb0286e66bc9691255842b0e4a505438cccf638545046f67e5839ada33dfb46e12854b385481285953cc4973bbcf896ccdf3962b010fc2fdb0ac6bfa7f61f0cc3c40f569bf668e96420d9e9da83056a79c9c784ab06190568b3098aaa38ea8e48ee27baeda8f34a764a02f1efaa278f536a3612a22db7de70c290bf8b625a148bc8a8dc5682bb8327b93072386b4b730c0ae072b2f78a01862de9530ef47b6446f2744556efa00da16f669a092fded8ca8410bea3eacd0b2901e4dea819cd45aa7553e2c7b852dad96b48c6904bf7eb57beeb2a08d711b0473f8c0f5bed4cd7577e92d0161b4ed87d55c1d5251c8a0a2c62d31401ade8e11f93fd97c958032f2954c226dbdbbdee86844b0ac38460d072396b11b96cfdb147152f92f98c906135fe666d5ed075be81b335694649b520a6b58af05d42e5f4bd8b25f3684ec9f4c49b968507bd83bb95c58efb6814ea0d145f3271b322da28640f21a07efc854d931e5ea4900190f0a4d68b70e110e6e30a6f7828d2938f23d5649c67b74eee398c49834a24076f7435629149cdbb051e337128880aca8338e8a12ad3fb49a2b7bbd9d88f2db9eb1e03a1a55e179f0e76a1b29004a1c9dedd51c06bce4673db0d0113c7b68a41f604b1bff92ee6a5974a98db294b81ca1d132894c5543b87dabcae0cdfa6ae762491d0e9d465f49d1e5294c59b6a5f22e4aaec8b4cc4d944c86f56013074a81de7c57e76b9245c18e06baaa3c1662fa57fdf4d760e404a5226e26e78b4a65b789ddd1e7d742f66c58ff4a945eaef462a89d5eb551095920926ae04de009dce18d18c60bfc87cf3ff78e526a8f0896f8b7b5ffd17ecff48126b6436e50690f046ffef46fe74b8ce33bfd81c62a0a91ea6ff2d7fbe323977e9338d6ae79937f9ae1396f1f506ba58a32ebc681b1b1b15c8d1ae58ea5a73200afbee07440c438de4dac1c3a49102ecbb32255bfdf0711ffcd6e2a1b20da16d3c0c1b03d473fbba151754f42e2241bd1a4e87990411ba7a769884c52080075b99c3e04f4141296cd6fbd81e6db0ac6e41e570794a14a6ab6d1e012358125631d3838fcad04af878e68aed3d66a7d2db78fc2fb684cae4089e7ed6e809f5b3d2efb741754657c27b4c8d1ad108b8f4159bdda4df5d67cb934f8bc96dd8c76bc8f917e3aa8a34d1fae7cb53e271a730cdf49796098988e418876244f1a24ba506e89f32accda60139c2118f1b34f45001d054a0df3bbf6141039f34985559346e2c06aae5cc3ff553a94166e41701f2893b5135e94908daa9f757d9e3d36b6c47fd152c00d92e800e4e3008bdaeffe91ca5667a408f93cd6ee109970a9321d29c9de04a0c3c11abaae47b8f25ce2bc42a6d08172c43eed671547f9457de6f67663368816b5c7bbac0c5167dd4e872b81eb019d7afa3d707d54fde3643897a4e0e25edb4367c825e1b936a44ee65ca9efe3ca05becb403da48778e5460ccb162bcbcfc05ebb8bb8183f43fc850e794f0c9862fe6f00472f0aa058d69def78b0b91fa5bf5b2a2dd01bc3390ff4f025622f59f4a21fa5ad76d18354734a4912d53d3d56bd80ba7cc9b3f32d96b9ef89d7fd627fd1f7f3ae2c42b8d4db48a12fcf27a6820779495a15b55a350378c4c7cc7dfe9adbd318a9959a722d50d22d18065a37ebedc673a63797fe182996327194bc023fe87adda945d5f142b63d7e20066ff3af171ddaf448b867dd2684d05ec22a1582de92e4f1bf08c69ea09e205eae16d69313aa60a987bc8eb3ae2fe0cec75fc67bd95c98e91e7085947453ab7a4e4ef8b0d593adb58372347a25c35f86298bcdf482309499359674b8463f34ec6fcb9b2eedbb9e189fc764287cd4cec68bde2cfcb556befcc0204cde044379bb85f5860a48b0f9d7e720774c7ee7f0374dec248b0c641b63b0faee54bcd074b4831f13c1e0b3d0c22b57d63d62a6b274fa37b1c96b27fbe4de1c74646bb6974f53fb7671025d51d175e90027bcba03d597c02e99e198a380edf8c6d56a81cca11efce3de08b69305e63e038e212fdea35180934233a384d08581ef2c297adbc6c09b928fad17d6784587ea978137f1db0cd8884cc027f1f9721d495894a2de652c40d2398ee9945c148d4e98523039eea6ebd8f7a59a6d7e4eefc69d4eafe8c424e0318217f20d50656bdd097c14c21fd1754d6d8582db1e4b6d172d2465997a55109783c6bc3d074dc73a5ad7eb70752e00bc5638fbcf2988c0821869be336945f0d3b5654cd607090b8839ad4a2a1e73aec9374768050a33c4a3b22c808b65d760bb3b9814bd67da31e86eca883242c0f7db8f5502792eed8444307c94bad791a003eb2e461dd31837b27a7c57444030a2dd41da1ffc297d07311fe822becec1b0b3a05b471d1859a1e37dce37c7934dbb53d76f91a7eb1183d0e51710e1b05d304cb175dae2c1cd818c4561853238c1393954045aab905385e5ffc7d2d04e4d408242250622c5fc95b3420c7dcab2080f9eb38380b5bb5e96deac405dc44931704bf86a7ea619ff99a3a04caa8c2a11ec05cd2af86cfad51417133b9edcc13fa0b69497ed027d953ea7dd7d7b07e181cde2237ed299cf4fb96dabf4ef218a7c10b9d9091edfd2e2b7f81f4c750f113dfc8d58dfbf3ef7344e251e98026f3e1260d6207c060fde760d978b27d6b7584ea8310a473b2ee2e7fcee24547875efe7a0a9e407e7ef4df41eefde885cd0f9c36c8813836538e27b04480b2719f60150d03152fdd5d0aee95a2e198d33b5855cce6596b80a895355d2752fc46304441ac00217fd94d9f72c7cc46dceed5bc02d2098d6376b40b991ac2e6c4a66d12093b00273db224873bf5c78abf00d15ee72ea9cb306cc0acaac751c679a941f1cb4f0a5622d2d1ffa6d04a4288b78688c37515d26797bcd38bb26977dc503f2460c40313217365299f786eae0916d0f45bfa71c5b493e3b33db6a3d148a1fcfad62c032be07b8c120173dabe65b34b53a7293cb2c2fddb487e6c5efcd283af070ea200a150a79ba6e78d1c430b4ce9ad6b7ee60c2e5213228f6c34fb862f000978820f7d81fce50383767a2782b57b316479846d73b65df0dd2e7f928e1b6500a51b0c92f8a4b4cf00ed9f3c636f34eb2c0c15eed717966e20b1ddd4a2748fd061b50f1a050b3e622db0f9608175922f911eae3658e0217b9980d37a2a3e5513122f0a6cfaa6f006ed0de105b2e01b404811c6a0a312a98e3f7448131f38a573a55306d1b08e17059a51ac3754574b384b4a3e887a09901cbf601343abb19e2dd55e0b18b595e6ee286a2b75b9678cd657acc7a97a76aa785832378d898757b480c702fea752014fcd94c8b1c263df27b7044bd68a5dc5d72120240de469ec6fcba8066f49831245eb2630699b7fe7c323f056ca3be7f08cff18e7948e2d3528064104c2dd743b5d82d2453fbe0b4b60bea247273d70392e4371a15be555e7ec3228014e800419d2bab85cbba0c19aa17f89f22bf37075e24be7c8e6d22063eea55f0e08e785aba5edca0bda72c90e40031c18ac6a5118dea585bc6cdece6fb9f1c807c18c0353979e1db4853bed48f9ee17e011c4642d2905ab9462ff8ec8e445ef09d92b7261583c6acee2b2856a5c1ecc77a17fef52b915bc549df4c0d89901830093764334181c8e14695e837c88dffacd9208af3d1a96dbebc618fe1f644e9f031c7bfb70de7a8cb9d2a2f96f1ff60c54dd071d15d0a1d3446b4b8e05adc27d090ba2ae982bfd598ab9e137cc77af6f25f8a7028ca0dc1e630106597029cbc3087c53534103a813ea6878222b0eb36a53909e726114c8d38f9c4479a784ed1205a831837d482e422dd3382d29c4df8bcb890e444731920361b7d4ef5a7ea8390b7e60d73824a063aaa4a6f5d66e9844d35eabae3d328c5508c4007af53347e20b7796026e1d926cab32dd540fbf2042d0a718133f9653f2899563c82bff1edd02906eb82c32647ae9801b591d6edb299169bd872146d537c42d1c3dc617fe9b02cbc0a2534fa1af48f6049a9dc2ef186e7503137a847003a48e7fd92da3224c002fa8015cdb5f5b3f4fa123bfd5d9cb2146bd6ec53e6b5423d2bc68cf55a33609428623de23756a627c73a3a139fafe314a9fa656ce37c5fdd3f1a0061f368001ec30623557e60dbc5e1a5af02447d683c4c29d3d13b3c4e872cec440894cc00d97fda53b617b99aa98fa11cd028855e4228fa3483c964a8504fc35dd07149b0ef1e69b83a86ca2ac9e13fb5fe60dc40b2a3f9dd28a5d40528da5b5cdcdea880579f1578e17aa5d569d0876be70f9b50a5823fe25916a1b58bc822cb63ceaf50ebe60fc31b1f66eb1a9249a4a3b56ca47b4aec90b00f72e61350bc1bdcdb26a02f23769c69157ceef36d93a486ba97d6fdc43905ea487b79cd8661663cc78950080a9269e32339932328825fcad1d38b3453d5e70044e17c59165de36d27b800708f9f689e8e8060ec53c2db2f4fed8045a16107ba69cdf073f48386d0028f909d3c963c50d73fd8d77b2c2121bdc753247f3d059b5f339f2f876fc692b41530c992177ca58fab13892cf7ec2a5c0b124b4b813c51ed454d57dc8d72fc2fdf2037cb80e92340ab80b778e4727ddf2f4264443fa70df6f53f27ee5584242938f08b13c7ed0d77f76969d1ffad74ea72642f4eef330a8d741e6db769db54f448700c38f237380ed439a5e6f5a606060fe1d7654475f755b1e897ca56ded4db68201a34993f3020c95f4686437b46cdb4c2b889b9ab37485b6b74c85aad5b7eb450935fa44eb2379c7bff5ef283561e0a599c0a95c254aa9dd99fc8c6707619112016c4ef4c0a67a3520ee39db31d6fecac6da3849fb93a6994a8871db42b41fec000ea55ac0245ab21c15d7066ec1265ae0b3005e93ce4cc12e78d81c8de24a7b0abd213b5156af44fd9b5737065a91913269c8b66080afde7c0bddd7455b78c8047816c3c3cc61ae7d8ba3de6cdc4315991c900f0c20b933233c857abc3fbede066f3c119e48da1d3850e2a0d1f16955be403c52d0c20b1bd6a066a9ef16a00f0599bf708e0cca36f73754654c64f2030f081dfdb1f4cdabc5921d677e595d930b417276e76eebff280ab8995e82087bb5f090ff9c6bf738e1aa4f2303978efd09364c69b289f36180f4fd98b3a72e5697b9d4965bafd40b3f040df70d934c550f15a1a7c7cef8d0a1354b192c4bc028d49446a64eb4c15fc88f4efd54bdd14d007490d8a6e5c3b964a21b3ffe0796deddebeaa5fd08a27d7de76c8253c887e10bf6eec0fe0be82fc1a984d0075a2127eca9ec10895e01cd2d91a950a9e5dcfb0890fe11a17f8a0189ec61fda540635c80146c6d7538cba436a9b1234a75a2f101a5891ed5d23a1a01c6825a6bca6a4ea6455ba8b5858c4458f2856ca2f766860ee07368d6d4ba9655d7058e4d1a1343dad25a6292d2100f52156476fa2a330c03cbb45d3da93ce69ded3963d2833c9b54fae3450084b7dbcce0775fae3bb48102652c005dc6f7f335ebcad3a64d96006a1720bd116bfe3262d77f08495bccf605265218c83a2f4fe5097e5467d7ffa7c77d99eeed7ccaa502146d0e7a8bb22f01759677ca90231cf18ee5fa685e6032b956afc75306154dc7018336e66626220e1d874d8eaa8542cd8b7bf0c8b658637211b947347fc2846d22d029fee52ca7054ec72a845048491e5cb39ab2297175d2a8f8a04c37a1a76181158859e3276d0b80d9fb0dd6a7c8b2a7b8c3ddd1041a1f310949af5ee9bff7f9a8d2fbfe03d602617e701eb517672ec5c337b32f7452c91b483ea64656968a8ef2f2a01c21a20bc63b8f899cb7bd89475571ca6669b45d75bf9b99b4edfb01082307e00c271909abece7b46044981e159237a901785f66d816a3d40b12561f87bc14d5f458630135030ede930d889ad9c9d2405c96e5fff91e6874692722b0cc237567c5a9f701f951cb813bfec760ef85fa37e80fddc2567520dc69c013a85be45c502bf71d0dc7035e3976be2a828419d16dc24e56dc852715feb54bbfe5cc17ce2bc3e4580c9649b23f854aea5b3214b1e0ac068dc40203ee83714034f44e8c661c4b340b0c12439533b1edf33aa6760526ebe2256fb7c39232dfafdcc6b27a32c9fa3255094ce64fcd1bbdf08153ecee4bc7ec80d38c12c04541da8670414afb837e4e530014163496ea2e26537169cbdb3a9748b8b859be5e0c05e22cef538123f119d8069b652919fc4f2e424e3f36856f60909d9ec6bdcd7ce240514b54ccbaa31d980cf1db2129c5798bac84ff1ddbdedf20ccc7cd045d83b5200f0ca71fc187829c0eaaba5092a6e44ea351fac4639d0b06761c6e237ae480e17f1fe564670073510a1dea04ece829d5810a27da0c752889d708e317914e8125614be1b86b91288309935a2fb047c314999266cec8ffc8e150c7af621c4fc29fdc171e3a8c1e830008df790c9ab6e9a38bc727a55e09a3b2b8338f3112fbd55cc6d0e99e3c4ed8c805e72c581dc442ca5b990a1194c991a0fd695965cd384df4aa6ae56e22013ec9086257546b5c0c80ccc6988c2823efe5c3df9f56e46c02d06ea005ec87d0a82d05aa865413ce72383cb97e53e839baebe772f853bbef1045ad61032d395d720c01d6b704a4934e76104b7e9e9f911cebd5b63f30240c94f8b845d510b238f7df07c2665726cab29244abd27e567bfce2210af456c1e7f4011b0afa721b9b7db4081aa85bb14b05c641efac96e95f0b8670615c7f5b51a89d9b5765602958194e02b4eeeccc9ada492bb18e004bcdaf540ad580c56c67cac9514134162327dba00ffb5f5c0e2aea231bc766ff775a338111cc4ff2d7cd8210a49a960ad1ab6d4201403f4459d2f4a1dd38a5eb8c12d3bdfd74f862c1a4a20b7f798adb5436c3080d5f0b60ccb0e0b8c707b6aab21354de2207b0b571a8bd0d0f83454ca88fc2fb0cda191d11afacc3b4c53a08f10f9f1b608cf87c936acc4d950d872684941ad70e2cebf598ad7da4e5abb9e0f8f2a0df9632584e5c152c7e7ca5899df49d72d70ed04a44760a5f6a95a7d9b4b463bbc769b5139c81e26e2ccb0e9aff0a65cd810db400d73e9fd5fd68e21484d76aed1cb1c2ea74e904f3daf968556522b7e556040a271dbb3c8be7bb7d4c8a182aba5917da53e599e63d998f3cbae48db434ca0047dd5668c13759e98aac326ba232eeedad039074e821e088ddb3d90cc330f102e945fe5fc390d99cc2447808c02a0942148601c301c2b2bae062764384b0090fe3ed8c22bc3a8f2338759d7262b5bb11261988693d0dd86bf74f68d3d5465c05b4e22e1dbcdf44d14dab07d630ed94d41f1eafd7afd939af89e2c80843f73a0bdf8e0d20eab3ef9de4368942800704a1306ebac3890019917724f3bd917ac8081f49407110f1550fd73806fa2cab1b1b94f4ddb458d358b4f3bdced21a64390f2461c1ddaba69f53de559680d491c9431456caebf89b65a94d5d4064accbff0cd5a9ef4a3a3ac583ebf6c328f3b34b0bff544ba710f8bfaf42dad1f8fe8df20b050071af8121d01b9dc2e8358d381c72ca1f82748c76351c6e9f26b6edb2b80ce144262fb0b9c37cda15cd44b7252f8c22a96f30a6b4ac1e40660e0d86b8de052af2ef97a86d42086db377358704218d13817b0908886ed51afd0b8a2198b90ddf675589d58f79ed381cabe6ab89f54d6c7491742fc44279f2b1167d71e2810d2662ff3b30b81a4c48edb1bc60e347ee049ad7880ab3baf335480a8b4224900674586552bd56af6b8a03efa8e3ee191301f856bcd7defb2a6ca1be5b685dd301588294d7f518c9331ffa921d08532dc9b4f31f4487715e1fada94f96408d2105f74c19c6183ae75b3511f8cb8b8736f0cb73da38823b1ca042e3ca795c1ca6091255a7c0415a02b36eabbe8a7fb294a8de62cb7c0cf027f527aa3bfccf92fbcafafeb804c69eff564dba1d2e50208c26c0318ded62d50f2d401baa0e8eae2af0749864721f41a4347a87524191e71cac9882c15a20551cf86c9e8c551f7f6cb12d1b98e9f3234c24b35a3821552dfb1af903756940d3c1c3d7d74981cec57f7316012933e7e97b5f4f215bc25d272a808e926803a5fb480c8fe3c56722b156a1c0081cb8282b97aac9e1bce78af155821e27cd69098801f0ac18e5a44a409baf649a4af5950850a677f5027ba4d99f84756f07b51f5a7584df60c27d9f54a63b72fe33abc1eaa0eda7abe0f4ef4e7c6d44f3525155754388788c6b2e1fe292237b27f877a4f0205a94dbf26cb529a85e789ab751ad487780b6c609ed58b123f0ae6d314c7e319cf02f51f42b1755471ef4c872f40a64523323a1af165a4e99f5c647bc2bd52867ed33c603dffa9a955769825f6820687aaba3ec880822d35bbeee95257dcdd3b5d530cd583f0dbe25fea5ed232657079c7f53f964430e19321f94154989ddcffdf969be9c8972e0d987473ec5cd581987d9f984c8e2144264a6a79ccafbc1f3844f4bc8f48729ed420a55155c6ee6bd0c32344c9eccf8c31f7959ec4fde9e3f2408e6904c750c115acfecd915b15d475728666d8fd2648d1c84dd7bffa162d952e57643ef58521f0d0a89c6510f52ea7e4fa03a44408dbfe3e02981f8ddb2c02f377e90b0a757ad7bc04073a3216d8605d8bdd8985f142bfe2b739380d1023f2f8540d1f5a88db456534830dbc137d2b17b738bc86825b793c8e8f5eb3452722a97a29b6f4e390263dfb04887effd805d92e1a3f6f950a8670a0684d2479d050077fa7464248fc50ae5d7ba8cc49c83ade9583e08f31626051b6a946f1baf9a86db3fdc637da0a3b52271c769ac16f447a13f1ca2772f999ea600ecc8db12fba453defa6d230b5ddc3950f0a67b7ea7e811cb77edf8c040719b1928a31ddd374956107616e71bbf80efc12487e5d07a30bec5ace04857f92aee02fb0b080b725c6a8183f17b4c2c3ce58be23115864516f18004f081f2823551dfa47121b5095b7a1243f746cbe323b2c6a8b5432b9bc93858a737f23b2181bdd6c5a0f9b56958bdb6cb3cd6b7de6fae0a48f51b10dd4cd521f0908d70070e55ba66d09706a5d2a2216f458c22e43a1d28831272c6198cfe216f39cbaafa785dac7eebbdd03e1b25998e9a242d763382df8ffbc2da141a886979823e6f0aef1caf68065a8c0f0b8a31c40dde8d8474493caf6e5fcf5256372f833040d8982227640832a5153b1ebad8ac31218305b7bb9a8c60ef5d7dd99da0d2407d3d4e94d71dec6634710d47f7221cd43b573a6dea97445ab5e682b924c8c2b53b64b749963b70b0c28deae280bb0ed788b82b334b3a322603588952e635fde46d8203ec786e1d7003c80e4d48600752a6b4f17b2838362331b833e58844f5a0b930b4a9e47d3f27e75bb4b25d16efc4032e38128a1c27b211502883bcd9ce76e2d408681e897a969fd2b5dd7a99f1a5697cc3272804db3dda88bbcfd42881ff2fb35db3b959c373d0b724d506f6a09227f0a0a03047fe87cb6f383588d5b2efaec03c6462f587d2c5d464aacb83affcf1b920d55d7ad20f66eff6519fda44b6dfabeb1dac1a42ed82ca05c7437b0149c2a9342273b8bef9a8bb752340358a6026fe65ba35abd7274f9fd15cbf6c216d677ae3f2e79590ba380425fd456a2a449c1e6a75c011f7f4a5e2bd3af1ebeb8f8e604bd856901088270ebc66ece4620a817436acc735067856326676d21259090f7c5b94efc0aaae27ec85935638a51bb03f654542ba449ea418d0561f5544d4ca935213f93531e32da144852e01ea175595a3eee0dbe9668e11a2b3c16ec757dc5544bf805d4e8581b6a6c503445b2280552f27feef5f3b9a00affb99d41c29bcfb4739d5eede1ea39050a10ca38d7baf6b8d92eb2083a64b1834786587c3d7bd6c487168971529ceb4c247a65c696462c8565f1d1a294d9db7354c79270fdef5727ad75bcee051cf6d97a6765ae93165062fcb02789382a9b25c1241a27f5be3cf5a097e0296340bc7886dde6f61fcaa6e564d0e459dc0f2ddd2fd1a1d9c28e6c60d9cf8245f29a8370f2b50e35cc4a028cd97b31b4156277c7a07ba39925d87fb49c8e339726a9cee55e1d352cbde8a8321bfb2418a01dad5bac2bc750af2e69693fb3409e3e37aa5d03c51f1b6d80a18b6346d56e8d5df8b44e548e8f84f3a5a084b1bf9f18d20aa8fd7544acc3307a3cf8d125ec428d794381a1ddd75215d859850121cd17142fef4c5bbba3aa4133ebba4e2b882e1195b72cd35c830fcbc4329b0706409a11373d38783e4b6ff84299c209b2f3c8dc0bb96a19eff62d01659c67cd3b89c03433fe712c7845a482b224896fd934b5ab32388cc12bc28962a329051917fcd69a94aad1218b7af6bf585704ed7f666a22a836eff92995c318592443633f1d503ccc0bfb5439fa8f2865bd1523dbaa325c1bee60d7ebde407382919f13a7c3f7ebcbc52c79db3102160f88b4383f352a8818d9332ab790982611122fde600b95496f032cea27c87000f297f57f623836617ca19d44d8b5c22e072ca1a046aaba260f05626a46ed23ba5321098ea649186617e2eb69b3b0252eec1a5c5e49e86d9d0ef6a94b577a32b90fdfb6f0b9df67191140afc15a95f52a0d12df9afffd6c5546675eb26048d8901b901060760a22514857305cc13e3d22228d87d2aed8e71c4733c923ac6de8400b3bdc36180e5fd13d1a724f41cfbf8c7a95074cf85246d81ee2ada4bb1835262c5430062911bd485e48df3d1044d90f7184e76bc2b1f1f3ff7d7038844f6a8eb2b6d7f66d7728daa05d2306d05da7a1c62ec5ceac775d02aa67325c892b7e3f0d3569f013a1bdaa72e8afd0e8a35bee390f4c2c6c2ea738edfa1914529a7a3801aedca05cd45cfff6f93c1aa7fbd574e47687baaa8e9ff60cd18adde8764d059869f030827f307ddcdd69e5f7043cac0452c37aa4a56de6a2cd47e991c3953bbb748580c5483f7e958f07a4d6aa8bee4cb4d96ca9d9a42816866ec35724c434e21bcba05910cd00cc8e27cc6582202119b7bf2a6ad2b2b2940e88b9ca4b38571d1f4840c05f9a9b5453bf5891764df9617277b0034c2186ca2df2e1d7bea2eeba9b5e00ee483406ba6ac6a0a521d86720940d9cb2cb039cba4e2c57d28465899929c8603d2ced41f8327f09ccc4fcfe37efe073f4f04210a2655dbab840358c68640600a3602a29e5ce2cafcf014b6985198fc6f042808143e107ff9a757fb1b34c7850b33e7cd524d2095fd46eab557f7b2520e8241c04950d2b965400531addeaec60057515543e93e7df611f5db0e808ddcbe3ac4e81ec194b16fa26040159328ec02dd88e54b5c22f7e29e1c299107ced0e9b905715e6a627ee2a94a6228fccacc0b5ce31b70d6dd58ef12577945d789d10901ec789aeec4844737b49098e149c008", 34 | }, 35 | } 36 | for _, test := range tests { 37 | serializedTx, _ := hex.DecodeString(test.txHex) 38 | buffer := new(bytes.Buffer) 39 | buffer.Write(serializedTx) 40 | transaction, err := ParseTransaction(buffer) 41 | if err != nil { 42 | t.Errorf("%s: error parsing tx: %s", test.name, err) 43 | } 44 | if !transaction.rctSignature.VerifyRctSimple() { 45 | t.Errorf("%s: not verified", test.name) 46 | } 47 | pubkeys := make([][]CtKey, len(test.inputOutpoints)) 48 | for i, outPoint := range test.inputOutpoints { 49 | pubkeys[i] = make([]CtKey, len(outPoint)) 50 | for j, point := range outPoint { 51 | pubkeys[i][j] = CtKey{ 52 | destination: HexToKey(point), 53 | } 54 | } 55 | } 56 | transaction.ExpandTransaction(pubkeys) 57 | } 58 | } 59 | --------------------------------------------------------------------------------