github.com/dashpay/godash@v0.0.0-20160726055534-e038a21e0e3d/blockchain/compress.go (about) 1 // Copyright (c) 2015-2016 The btcsuite developers 2 // Copyright (c) 2016 The Dash developers 3 // Use of this source code is governed by an ISC 4 // license that can be found in the LICENSE file. 5 6 package blockchain 7 8 import ( 9 "github.com/dashpay/godash/btcec" 10 "github.com/dashpay/godash/txscript" 11 ) 12 13 // ----------------------------------------------------------------------------- 14 // A variable length quantity (VLQ) is an encoding that uses an arbitrary number 15 // of binary octets to represent an arbitrarily large integer. The scheme 16 // employs a most significant byte (MSB) base-128 encoding where the high bit in 17 // each byte indicates whether or not the byte is the final one. In addition, 18 // to ensure there are no redundant encodings, an offset is subtracted every 19 // time a group of 7 bits is shifted out. Therefore each integer can be 20 // represented in exactly one way, and each representation stands for exactly 21 // one integer. 22 // 23 // Another nice property of this encoding is that it provides a compact 24 // representation of values that are typically used to indicate sizes. For 25 // example, the values 0 - 127 are represented with a single byte, 128 - 16511 26 // with two bytes, and 16512 - 2113663 with three bytes. 27 // 28 // While the encoding allows arbitrarily large integers, it is artificially 29 // limited in this code to an unsigned 64-bit integer for efficiency purposes. 30 // 31 // Example encodings: 32 // 0 -> [0x00] 33 // 127 -> [0x7f] * Max 1-byte value 34 // 128 -> [0x80 0x00] 35 // 129 -> [0x80 0x01] 36 // 255 -> [0x80 0x7f] 37 // 256 -> [0x81 0x00] 38 // 16511 -> [0xff 0x7f] * Max 2-byte value 39 // 16512 -> [0x80 0x80 0x00] 40 // 32895 -> [0x80 0xff 0x7f] 41 // 2113663 -> [0xff 0xff 0x7f] * Max 3-byte value 42 // 270549119 -> [0xff 0xff 0xff 0x7f] * Max 4-byte value 43 // 2^64-1 -> [0x80 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0x7f] 44 // 45 // References: 46 // https://en.wikipedia.org/wiki/Variable-length_quantity 47 // http://www.codecodex.com/wiki/Variable-Length_Integers 48 // ----------------------------------------------------------------------------- 49 50 // serializeSizeVLQ returns the number of bytes it would take to serialize the 51 // passed number as a variable-length quantity according to the format described 52 // above. 53 func serializeSizeVLQ(n uint64) int { 54 size := 1 55 for ; n > 0x7f; n = (n >> 7) - 1 { 56 size++ 57 } 58 59 return size 60 } 61 62 // putVLQ serializes the provided number to a variable-length quantity according 63 // to the format described above and returns the number of bytes of the encoded 64 // value. The result is placed directly into the passed byte slice which must 65 // be at least large enough to handle the number of bytes returned by the 66 // serializeSizeVLQ function or it will panic. 67 func putVLQ(target []byte, n uint64) int { 68 offset := 0 69 for ; ; offset++ { 70 // The high bit is set when another byte follows. 71 highBitMask := byte(0x80) 72 if offset == 0 { 73 highBitMask = 0x00 74 } 75 76 target[offset] = byte(n&0x7f) | highBitMask 77 if n <= 0x7f { 78 break 79 } 80 n = (n >> 7) - 1 81 } 82 83 // Reverse the bytes so it is MSB-encoded. 84 for i, j := 0, offset; i < j; i, j = i+1, j-1 { 85 target[i], target[j] = target[j], target[i] 86 } 87 88 return offset + 1 89 } 90 91 // deserializeVLQ deserializes the provided variable-length quantity according 92 // to the format described above. It also returns the number of bytes 93 // deserialized. 94 func deserializeVLQ(serialized []byte) (uint64, int) { 95 var n uint64 96 var size int 97 for _, val := range serialized { 98 size++ 99 n = (n << 7) | uint64(val&0x7f) 100 if val&0x80 != 0x80 { 101 break 102 } 103 n++ 104 } 105 106 return n, size 107 } 108 109 // ----------------------------------------------------------------------------- 110 // In order to reduce the size of stored scripts, a domain specific compression 111 // algorithm is used which recognizes standard scripts and stores them using 112 // less bytes than the original script. The compression algorithm used here was 113 // obtained from Bitcoin Core, so all credits for the algorithm go to it. 114 // 115 // The general serialized format is: 116 // 117 // <script size or type><script data> 118 // 119 // Field Type Size 120 // script size or type VLQ variable 121 // script data []byte variable 122 // 123 // The specific serialized format for each recognized standard script is: 124 // 125 // - Pay-to-pubkey-hash: (21 bytes) - <0><20-byte pubkey hash> 126 // - Pay-to-script-hash: (21 bytes) - <1><20-byte script hash> 127 // - Pay-to-pubkey**: (33 bytes) - <2, 3, 4, or 5><32-byte pubkey X value> 128 // 2, 3 = compressed pubkey with bit 0 specifying the y coordinate to use 129 // 4, 5 = uncompressed pubkey with bit 0 specifying the y coordinate to use 130 // ** Only valid public keys starting with 0x02, 0x03, and 0x04 are supported. 131 // 132 // Any scripts which are not recognized as one of the aforementioned standard 133 // scripts are encoded using the general serialized format and encode the script 134 // size as the sum of the actual size of the script and the number of special 135 // cases. 136 // ----------------------------------------------------------------------------- 137 138 // The following constants specify the special constants used to identify a 139 // special script type in the domain-specific compressed script encoding. 140 // 141 // NOTE: This section specifically does not use iota since these values are 142 // serialized and must be stable for long-term storage. 143 const ( 144 // cstPayToPubKeyHash identifies a compressed pay-to-pubkey-hash script. 145 cstPayToPubKeyHash = 0 146 147 // cstPayToScriptHash identifies a compressed pay-to-script-hash script. 148 cstPayToScriptHash = 1 149 150 // cstPayToPubKeyComp2 identifies a compressed pay-to-pubkey script to 151 // a compressed pubkey. Bit 0 specifies which y-coordinate to use 152 // to reconstruct the full uncompressed pubkey. 153 cstPayToPubKeyComp2 = 2 154 155 // cstPayToPubKeyComp3 identifies a compressed pay-to-pubkey script to 156 // a compressed pubkey. Bit 0 specifies which y-coordinate to use 157 // to reconstruct the full uncompressed pubkey. 158 cstPayToPubKeyComp3 = 3 159 160 // cstPayToPubKeyUncomp4 identifies a compressed pay-to-pubkey script to 161 // an uncompressed pubkey. Bit 0 specifies which y-coordinate to use 162 // to reconstruct the full uncompressed pubkey. 163 cstPayToPubKeyUncomp4 = 4 164 165 // cstPayToPubKeyUncomp5 identifies a compressed pay-to-pubkey script to 166 // an uncompressed pubkey. Bit 0 specifies which y-coordinate to use 167 // to reconstruct the full uncompressed pubkey. 168 cstPayToPubKeyUncomp5 = 5 169 170 // numSpecialScripts is the number of special scripts recognized by the 171 // domain-specific script compression algorithm. 172 numSpecialScripts = 6 173 ) 174 175 // isPubKeyHash returns whether or not the passed public key script is a 176 // standard pay-to-pubkey-hash script along with the pubkey hash it is paying to 177 // if it is. 178 func isPubKeyHash(script []byte) (bool, []byte) { 179 if len(script) == 25 && script[0] == txscript.OP_DUP && 180 script[1] == txscript.OP_HASH160 && 181 script[2] == txscript.OP_DATA_20 && 182 script[23] == txscript.OP_EQUALVERIFY && 183 script[24] == txscript.OP_CHECKSIG { 184 185 return true, script[3:23] 186 } 187 188 return false, nil 189 } 190 191 // isScriptHash returns whether or not the passed public key script is a 192 // standard pay-to-script-hash script along with the script hash it is paying to 193 // if it is. 194 func isScriptHash(script []byte) (bool, []byte) { 195 if len(script) == 23 && script[0] == txscript.OP_HASH160 && 196 script[1] == txscript.OP_DATA_20 && 197 script[22] == txscript.OP_EQUAL { 198 199 return true, script[2:22] 200 } 201 202 return false, nil 203 } 204 205 // isPubKey returns whether or not the passed public key script is a standard 206 // pay-to-pubkey script that pays to a valid compressed or uncompressed public 207 // key along with the serialized pubkey it is paying to if it is. 208 // 209 // NOTE: This function ensures the public key is actually valid since the 210 // compression algorithm requires valid pubkeys. It does not support hybrid 211 // pubkeys. This means that even if the script has the correct form for a 212 // pay-to-pubkey script, this function will only return true when it is paying 213 // to a valid compressed or uncompressed pubkey. 214 func isPubKey(script []byte) (bool, []byte) { 215 // Pay-to-compressed-pubkey script. 216 if len(script) == 35 && script[0] == txscript.OP_DATA_33 && 217 script[34] == txscript.OP_CHECKSIG && (script[1] == 0x02 || 218 script[1] == 0x03) { 219 220 // Ensure the public key is valid. 221 serializedPubKey := script[1:34] 222 _, err := btcec.ParsePubKey(serializedPubKey, btcec.S256()) 223 if err == nil { 224 return true, serializedPubKey 225 } 226 } 227 228 // Pay-to-uncompressed-pubkey script. 229 if len(script) == 67 && script[0] == txscript.OP_DATA_65 && 230 script[66] == txscript.OP_CHECKSIG && script[1] == 0x04 { 231 232 // Ensure the public key is valid. 233 serializedPubKey := script[1:66] 234 _, err := btcec.ParsePubKey(serializedPubKey, btcec.S256()) 235 if err == nil { 236 return true, serializedPubKey 237 } 238 } 239 240 return false, nil 241 } 242 243 // compressedScriptSize returns the number of bytes the passed script would take 244 // when encoded with the domain specific compression algorithm described above. 245 func compressedScriptSize(pkScript []byte, version int32) int { 246 // Pay-to-pubkey-hash script. 247 if valid, _ := isPubKeyHash(pkScript); valid { 248 return 21 249 } 250 251 // Pay-to-script-hash script. 252 if valid, _ := isScriptHash(pkScript); valid { 253 return 21 254 } 255 256 // Pay-to-pubkey (compressed or uncompressed) script. 257 if valid, _ := isPubKey(pkScript); valid { 258 return 33 259 } 260 261 // When none of the above special cases apply, encode the script as is 262 // preceded by the sum of its size and the number of special cases 263 // encoded as a variable length quantity. 264 return serializeSizeVLQ(uint64(len(pkScript)+numSpecialScripts)) + 265 len(pkScript) 266 } 267 268 // decodeCompressedScriptSize treats the passed serialized bytes as a compressed 269 // script, possibly followed by other data, and returns the number of bytes it 270 // occupies taking into account the special encoding of the script size by the 271 // domain specific compression algorithm described above. 272 func decodeCompressedScriptSize(serialized []byte, version int32) int { 273 scriptSize, bytesRead := deserializeVLQ(serialized) 274 if bytesRead == 0 { 275 return 0 276 } 277 278 switch scriptSize { 279 case cstPayToPubKeyHash: 280 return 21 281 282 case cstPayToScriptHash: 283 return 21 284 285 case cstPayToPubKeyComp2, cstPayToPubKeyComp3, cstPayToPubKeyUncomp4, 286 cstPayToPubKeyUncomp5: 287 return 33 288 } 289 290 scriptSize -= numSpecialScripts 291 scriptSize += uint64(bytesRead) 292 return int(scriptSize) 293 } 294 295 // putCompressedScript compresses the passed script according to the domain 296 // specific compression algorithm described above directly into the passed 297 // target byte slice. The target byte slice must be at least large enough to 298 // handle the number of bytes returned by the compressedScriptSize function or 299 // it will panic. 300 func putCompressedScript(target, pkScript []byte, version int32) int { 301 // Pay-to-pubkey-hash script. 302 if valid, hash := isPubKeyHash(pkScript); valid { 303 target[0] = cstPayToPubKeyHash 304 copy(target[1:21], hash) 305 return 21 306 } 307 308 // Pay-to-script-hash script. 309 if valid, hash := isScriptHash(pkScript); valid { 310 target[0] = cstPayToScriptHash 311 copy(target[1:21], hash) 312 return 21 313 } 314 315 // Pay-to-pubkey (compressed or uncompressed) script. 316 if valid, serializedPubKey := isPubKey(pkScript); valid { 317 pubKeyFormat := serializedPubKey[0] 318 switch pubKeyFormat { 319 case 0x02, 0x03: 320 target[0] = pubKeyFormat 321 copy(target[1:33], serializedPubKey[1:33]) 322 return 33 323 case 0x04: 324 // Encode the oddness of the serialized pubkey into the 325 // compressed script type. 326 target[0] = pubKeyFormat | (serializedPubKey[64] & 0x01) 327 copy(target[1:33], serializedPubKey[1:33]) 328 return 33 329 } 330 } 331 332 // When none of the above special cases apply, encode the unmodified 333 // script preceded by the sum of its size and the number of special 334 // cases encoded as a variable length quantity. 335 encodedSize := uint64(len(pkScript) + numSpecialScripts) 336 vlqSizeLen := putVLQ(target, encodedSize) 337 copy(target[vlqSizeLen:], pkScript) 338 return vlqSizeLen + len(pkScript) 339 } 340 341 // decompressScript returns the original script obtained by decompressing the 342 // passed compressed script according to the domain specific compression 343 // algorithm described above. 344 // 345 // NOTE: The script parameter must already have been proven to be long enough 346 // to contain the number of bytes returned by decodeCompressedScriptSize or it 347 // will panic. This is acceptable since it is only an internal function. 348 func decompressScript(compressedPkScript []byte, version int32) []byte { 349 // In practice this function will not be called with a zero-length or 350 // nil script since the nil script encoding includes the length, however 351 // the code below assumes the length exists, so just return nil now if 352 // the function ever ends up being called with a nil script in the 353 // future. 354 if len(compressedPkScript) == 0 { 355 return nil 356 } 357 358 // Decode the script size and examine it for the special cases. 359 encodedScriptSize, bytesRead := deserializeVLQ(compressedPkScript) 360 switch encodedScriptSize { 361 // Pay-to-pubkey-hash script. The resulting script is: 362 // <OP_DUP><OP_HASH160><20 byte hash><OP_EQUALVERIFY><OP_CHECKSIG> 363 case cstPayToPubKeyHash: 364 pkScript := make([]byte, 25) 365 pkScript[0] = txscript.OP_DUP 366 pkScript[1] = txscript.OP_HASH160 367 pkScript[2] = txscript.OP_DATA_20 368 copy(pkScript[3:], compressedPkScript[bytesRead:bytesRead+20]) 369 pkScript[23] = txscript.OP_EQUALVERIFY 370 pkScript[24] = txscript.OP_CHECKSIG 371 return pkScript 372 373 // Pay-to-script-hash script. The resulting script is: 374 // <OP_HASH160><20 byte script hash><OP_EQUAL> 375 case cstPayToScriptHash: 376 pkScript := make([]byte, 23) 377 pkScript[0] = txscript.OP_HASH160 378 pkScript[1] = txscript.OP_DATA_20 379 copy(pkScript[2:], compressedPkScript[bytesRead:bytesRead+20]) 380 pkScript[22] = txscript.OP_EQUAL 381 return pkScript 382 383 // Pay-to-compressed-pubkey script. The resulting script is: 384 // <OP_DATA_33><33 byte compressed pubkey><OP_CHECKSIG> 385 case cstPayToPubKeyComp2, cstPayToPubKeyComp3: 386 pkScript := make([]byte, 35) 387 pkScript[0] = txscript.OP_DATA_33 388 pkScript[1] = byte(encodedScriptSize) 389 copy(pkScript[2:], compressedPkScript[bytesRead:bytesRead+32]) 390 pkScript[34] = txscript.OP_CHECKSIG 391 return pkScript 392 393 // Pay-to-uncompressed-pubkey script. The resulting script is: 394 // <OP_DATA_65><65 byte uncompressed pubkey><OP_CHECKSIG> 395 case cstPayToPubKeyUncomp4, cstPayToPubKeyUncomp5: 396 // Change the leading byte to the appropriate compressed pubkey 397 // identifier (0x02 or 0x03) so it can be decoded as a 398 // compressed pubkey. This really should never fail since the 399 // encoding ensures it is valid before compressing to this type. 400 compressedKey := make([]byte, 33) 401 compressedKey[0] = byte(encodedScriptSize - 2) 402 copy(compressedKey[1:], compressedPkScript[1:]) 403 key, err := btcec.ParsePubKey(compressedKey, btcec.S256()) 404 if err != nil { 405 return nil 406 } 407 408 pkScript := make([]byte, 67) 409 pkScript[0] = txscript.OP_DATA_65 410 copy(pkScript[1:], key.SerializeUncompressed()) 411 pkScript[66] = txscript.OP_CHECKSIG 412 return pkScript 413 } 414 415 // When none of the special cases apply, the script was encoded using 416 // the general format, so reduce the script size by the number of 417 // special cases and return the unmodified script. 418 scriptSize := int(encodedScriptSize - numSpecialScripts) 419 pkScript := make([]byte, scriptSize) 420 copy(pkScript, compressedPkScript[bytesRead:bytesRead+scriptSize]) 421 return pkScript 422 } 423 424 // ----------------------------------------------------------------------------- 425 // In order to reduce the size of stored amounts, a domain specific compression 426 // algorithm is used which relies on there typically being a lot of zeroes at 427 // end of the amounts. The compression algorithm used here was obtained from 428 // Bitcoin Core, so all credits for the algorithm go to it. 429 // 430 // While this is simply exchanging one uint64 for another, the resulting value 431 // for typical amounts has a much smaller magnitude which results in fewer bytes 432 // when encoded as variable length quantity. For example, consider the amount 433 // of 0.1 BTC which is 10000000 satoshi. Encoding 10000000 as a VLQ would take 434 // 4 bytes while encoding the compressed value of 8 as a VLQ only takes 1 byte. 435 // 436 // Essentially the compression is achieved by splitting the value into an 437 // exponent in the range [0-9] and a digit in the range [1-9], when possible, 438 // and encoding them in a way that can be decoded. More specifically, the 439 // encoding is as follows: 440 // - 0 is 0 441 // - Find the exponent, e, as the largest power of 10 that evenly divides the 442 // value up to a maximum of 9 443 // - When e < 9, the final digit can't be 0 so store it as d and remove it by 444 // dividing the value by 10 (call the result n). The encoded value is thus: 445 // 1 + 10*(9*n + d-1) + e 446 // - When e==9, the only thing known is the amount is not 0. The encoded value 447 // is thus: 448 // 1 + 10*(n-1) + e == 10 + 10*(n-1) 449 // 450 // Example encodings: 451 // (The numbers in parenthesis are the number of bytes when serialized as a VLQ) 452 // 0 (1) -> 0 (1) * 0.00000000 BTC 453 // 1000 (2) -> 4 (1) * 0.00001000 BTC 454 // 10000 (2) -> 5 (1) * 0.00010000 BTC 455 // 12345678 (4) -> 111111101(4) * 0.12345678 BTC 456 // 50000000 (4) -> 47 (1) * 0.50000000 BTC 457 // 100000000 (4) -> 9 (1) * 1.00000000 BTC 458 // 500000000 (5) -> 49 (1) * 5.00000000 BTC 459 // 1000000000 (5) -> 10 (1) * 10.00000000 BTC 460 // ----------------------------------------------------------------------------- 461 462 // compressTxOutAmount compresses the passed amount according to the domain 463 // specific compression algorithm described above. 464 func compressTxOutAmount(amount uint64) uint64 { 465 // No need to do any work if it's zero. 466 if amount == 0 { 467 return 0 468 } 469 470 // Find the largest power of 10 (max of 9) that evenly divides the 471 // value. 472 exponent := uint64(0) 473 for amount%10 == 0 && exponent < 9 { 474 amount /= 10 475 exponent++ 476 } 477 478 // The compressed result for exponents less than 9 is: 479 // 1 + 10*(9*n + d-1) + e 480 if exponent < 9 { 481 lastDigit := amount % 10 482 amount /= 10 483 return 1 + 10*(9*amount+lastDigit-1) + exponent 484 } 485 486 // The compressed result for an exponent of 9 is: 487 // 1 + 10*(n-1) + e == 10 + 10*(n-1) 488 return 10 + 10*(amount-1) 489 } 490 491 // decompressTxOutAmount returns the original amount the passed compressed 492 // amount represents according to the domain specific compression algorithm 493 // described above. 494 func decompressTxOutAmount(amount uint64) uint64 { 495 // No need to do any work if it's zero. 496 if amount == 0 { 497 return 0 498 } 499 500 // The decompressed amount is either of the following two equations: 501 // x = 1 + 10*(9*n + d - 1) + e 502 // x = 1 + 10*(n - 1) + 9 503 amount-- 504 505 // The decompressed amount is now one of the following two equations: 506 // x = 10*(9*n + d - 1) + e 507 // x = 10*(n - 1) + 9 508 exponent := amount % 10 509 amount /= 10 510 511 // The decompressed amount is now one of the following two equations: 512 // x = 9*n + d - 1 | where e < 9 513 // x = n - 1 | where e = 9 514 n := uint64(0) 515 if exponent < 9 { 516 lastDigit := amount%9 + 1 517 amount /= 9 518 n = amount*10 + lastDigit 519 } else { 520 n = amount + 1 521 } 522 523 // Apply the exponent. 524 for ; exponent > 0; exponent-- { 525 n *= 10 526 } 527 528 return n 529 } 530 531 // ----------------------------------------------------------------------------- 532 // Compressed transaction outputs consist of an amount and a public key script 533 // both compressed using the domain specific compression algorithms previously 534 // described. 535 // 536 // The serialized format is: 537 // 538 // <compressed amount><compressed script> 539 // 540 // Field Type Size 541 // compressed amount VLQ variable 542 // compressed script []byte variable 543 // ----------------------------------------------------------------------------- 544 545 // compressedTxOutSize returns the number of bytes the passed transaction output 546 // fields would take when encoded with the format described above. The 547 // preCompressed flag indicates the provided amount and script are already 548 // compressed. This is useful since loaded utxo entries are not decompressed 549 // until the output is accessed. 550 func compressedTxOutSize(amount uint64, pkScript []byte, version int32, preCompressed bool) int { 551 if preCompressed { 552 return serializeSizeVLQ(amount) + len(pkScript) 553 } 554 555 return serializeSizeVLQ(compressTxOutAmount(amount)) + 556 compressedScriptSize(pkScript, version) 557 } 558 559 // putCompressedTxOut potentially compresses the passed amount and script 560 // according to their domain specific compression algorithms and encodes them 561 // directly into the passed target byte slice with the format described above. 562 // The preCompressed flag indicates the provided amount and script are already 563 // compressed in which case the values are not modified. This is useful since 564 // loaded utxo entries are not decompressed until the output is accessed. The 565 // target byte slice must be at least large enough to handle the number of bytes 566 // returned by the compressedTxOutSize function or it will panic. 567 func putCompressedTxOut(target []byte, amount uint64, pkScript []byte, version int32, preCompressed bool) int { 568 if preCompressed { 569 offset := putVLQ(target, amount) 570 copy(target[offset:], pkScript) 571 return offset + len(pkScript) 572 } 573 574 offset := putVLQ(target, compressTxOutAmount(amount)) 575 offset += putCompressedScript(target[offset:], pkScript, version) 576 return offset 577 } 578 579 // decodeCompressedTxOut decodes the passed compressed txout, possibly followed 580 // by other data, into its compressed amount and compressed script and returns 581 // them along with the number of bytes they occupied. 582 func decodeCompressedTxOut(serialized []byte, version int32) (uint64, []byte, int, error) { 583 // Deserialize the compressed amount and ensure there are bytes 584 // remaining for the compressed script. 585 compressedAmount, bytesRead := deserializeVLQ(serialized) 586 if bytesRead >= len(serialized) { 587 return 0, nil, bytesRead, errDeserialize("unexpected end of " + 588 "data after compressed amount") 589 } 590 591 // Decode the compressed script size and ensure there are enough bytes 592 // left in the slice for it. 593 scriptSize := decodeCompressedScriptSize(serialized[bytesRead:], version) 594 if len(serialized[bytesRead:]) < scriptSize { 595 return 0, nil, bytesRead, errDeserialize("unexpected end of " + 596 "data after script size") 597 } 598 599 // Make a copy of the compressed script so the original serialized data 600 // can be released as soon as possible. 601 compressedScript := make([]byte, scriptSize) 602 copy(compressedScript, serialized[bytesRead:bytesRead+scriptSize]) 603 return compressedAmount, compressedScript, bytesRead + scriptSize, nil 604 }