github.com/decred/dcrd/blockchain@v1.2.1/compress.go (about) 1 // Copyright (c) 2015-2016 The btcsuite developers 2 // Copyright (c) 2015-2018 The Decred 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 "fmt" 10 11 "github.com/decred/dcrd/blockchain/stake" 12 "github.com/decred/dcrd/dcrec/secp256k1" 13 "github.com/decred/dcrd/txscript" 14 ) 15 16 // currentCompressionVersion is the current script compression version of the 17 // database. 18 const currentCompressionVersion = 1 19 20 // ----------------------------------------------------------------------------- 21 // A variable length quantity (VLQ) is an encoding that uses an arbitrary number 22 // of binary octets to represent an arbitrarily large integer. The scheme 23 // employs a most significant byte (MSB) base-128 encoding where the high bit in 24 // each byte indicates whether or not the byte is the final one. In addition, 25 // to ensure there are no redundant encodings, an offset is subtracted every 26 // time a group of 7 bits is shifted out. Therefore each integer can be 27 // represented in exactly one way, and each representation stands for exactly 28 // one integer. 29 // 30 // Another nice property of this encoding is that it provides a compact 31 // representation of values that are typically used to indicate sizes. For 32 // example, the values 0 - 127 are represented with a single byte, 128 - 16511 33 // with two bytes, and 16512 - 2113663 with three bytes. 34 // 35 // While the encoding allows arbitrarily large integers, it is artificially 36 // limited in this code to an unsigned 64-bit integer for efficiency purposes. 37 // 38 // Example encodings: 39 // 0 -> [0x00] 40 // 127 -> [0x7f] * Max 1-byte value 41 // 128 -> [0x80 0x00] 42 // 129 -> [0x80 0x01] 43 // 255 -> [0x80 0x7f] 44 // 256 -> [0x81 0x00] 45 // 16511 -> [0xff 0x7f] * Max 2-byte value 46 // 16512 -> [0x80 0x80 0x00] 47 // 32895 -> [0x80 0xff 0x7f] 48 // 2113663 -> [0xff 0xff 0x7f] * Max 3-byte value 49 // 270549119 -> [0xff 0xff 0xff 0x7f] * Max 4-byte value 50 // 2^64-1 -> [0x80 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0x7f] 51 // 52 // References: 53 // https://en.wikipedia.org/wiki/Variable-length_quantity 54 // http://www.codecodex.com/wiki/Variable-Length_Integers 55 // ----------------------------------------------------------------------------- 56 57 // serializeSizeVLQ returns the number of bytes it would take to serialize the 58 // passed number as a variable-length quantity according to the format described 59 // above. 60 func serializeSizeVLQ(n uint64) int { 61 size := 1 62 for ; n > 0x7f; n = (n >> 7) - 1 { 63 size++ 64 } 65 66 return size 67 } 68 69 // putVLQ serializes the provided number to a variable-length quantity according 70 // to the format described above and returns the number of bytes of the encoded 71 // value. The result is placed directly into the passed byte slice which must 72 // be at least large enough to handle the number of bytes returned by the 73 // serializeSizeVLQ function or it will panic. 74 func putVLQ(target []byte, n uint64) int { 75 offset := 0 76 for ; ; offset++ { 77 // The high bit is set when another byte follows. 78 highBitMask := byte(0x80) 79 if offset == 0 { 80 highBitMask = 0x00 81 } 82 83 target[offset] = byte(n&0x7f) | highBitMask 84 if n <= 0x7f { 85 break 86 } 87 n = (n >> 7) - 1 88 } 89 90 // Reverse the bytes so it is MSB-encoded. 91 for i, j := 0, offset; i < j; i, j = i+1, j-1 { 92 target[i], target[j] = target[j], target[i] 93 } 94 95 return offset + 1 96 } 97 98 // deserializeVLQ deserializes the provided variable-length quantity according 99 // to the format described above. It also returns the number of bytes 100 // deserialized. 101 func deserializeVLQ(serialized []byte) (uint64, int) { 102 var n uint64 103 var size int 104 for _, val := range serialized { 105 size++ 106 n = (n << 7) | uint64(val&0x7f) 107 if val&0x80 != 0x80 { 108 break 109 } 110 n++ 111 } 112 113 return n, size 114 } 115 116 // ----------------------------------------------------------------------------- 117 // In order to reduce the size of stored scripts, a domain specific compression 118 // algorithm is used which recognizes standard scripts and stores them using 119 // less bytes than the original script. The compression algorithm used here was 120 // obtained from Bitcoin Core, so all credits for the algorithm go to it. 121 // 122 // The general serialized format is: 123 // 124 // <script size or type><script data> 125 // 126 // Field Type Size 127 // script size or type VLQ variable 128 // script data []byte variable 129 // 130 // The specific serialized format for each recognized standard script is: 131 // 132 // - Pay-to-pubkey-hash: (21 bytes) - <0><20-byte pubkey hash> 133 // - Pay-to-script-hash: (21 bytes) - <1><20-byte script hash> 134 // - Pay-to-pubkey**: (33 bytes) - <2, 3, 4, or 5><32-byte pubkey X value> 135 // 2, 3 = compressed pubkey with bit 0 specifying the y coordinate to use 136 // 4, 5 = uncompressed pubkey with bit 0 specifying the y coordinate to use 137 // ** Only valid public keys starting with 0x02, 0x03, and 0x04 are supported. 138 // 139 // Any scripts which are not recognized as one of the aforementioned standard 140 // scripts are encoded using the general serialized format and encode the script 141 // size as the sum of the actual size of the script and the number of special 142 // cases. 143 // ----------------------------------------------------------------------------- 144 145 // The following constants specify the special constants used to identify a 146 // special script type in the domain-specific compressed script encoding. 147 // 148 // NOTE: This section specifically does not use iota since these values are 149 // serialized and must be stable for long-term storage. 150 const ( 151 // cstPayToPubKeyHash identifies a compressed pay-to-pubkey-hash script. 152 cstPayToPubKeyHash = 0 153 154 // cstPayToScriptHash identifies a compressed pay-to-script-hash script. 155 cstPayToScriptHash = 1 156 157 // cstPayToPubKeyCompEven identifies a compressed pay-to-pubkey script to 158 // a compressed pubkey whose y coordinate is not odd. 159 cstPayToPubKeyCompEven = 2 160 161 // cstPayToPubKeyCompOdd identifies a compressed pay-to-pubkey script to 162 // a compressed pubkey whose y coordinate is odd. 163 cstPayToPubKeyCompOdd = 3 164 165 // cstPayToPubKeyUncompEven identifies a compressed pay-to-pubkey script to 166 // an uncompressed pubkey whose y coordinate is not odd when compressed. 167 cstPayToPubKeyUncompEven = 4 168 169 // cstPayToPubKeyUncompOdd identifies a compressed pay-to-pubkey script to 170 // an uncompressed pubkey whose y coordinate is odd when compressed. 171 cstPayToPubKeyUncompOdd = 5 172 173 // numSpecialScripts is the number of special scripts possibly recognized 174 // by the domain-specific script compression algorithm. It is one more 175 // than half the number required to overflow a single byte in VLQ format 176 // (127). All scripts prefixed 64 and higher for their size are considered 177 // uncompressed scripts that are stored uncompressed. Because only 5 178 // special script types are currently stored by Decred, there is a large 179 // amount of room for future upgrades to the compression algorithm with 180 // scripts that are common, such as those for the staking system. 181 numSpecialScripts = 64 182 ) 183 184 // extractPubKeyHash extracts a pubkey hash that is being paid from the passed 185 // public key script if it is a standard pay-to-pubkey-hash script. It will 186 // return nil otherwise. 187 func extractPubKeyHash(script []byte) []byte { 188 if len(script) == 25 && script[0] == txscript.OP_DUP && 189 script[1] == txscript.OP_HASH160 && 190 script[2] == txscript.OP_DATA_20 && 191 script[23] == txscript.OP_EQUALVERIFY && 192 script[24] == txscript.OP_CHECKSIG { 193 194 return script[3:23] 195 } 196 197 return nil 198 } 199 200 // isPubKeyHash returns whether or not the passed public key script is a 201 // standard pay-to-pubkey-hash script. 202 func isPubKeyHash(script []byte) bool { 203 return extractPubKeyHash(script) != nil 204 } 205 206 // extractScriptHash extracts a script hash that is being paid from the passed 207 // public key script if it is a standard pay-to-script-hash script. It will 208 // return nil otherwise. 209 func extractScriptHash(script []byte) []byte { 210 if len(script) == 23 && script[0] == txscript.OP_HASH160 && 211 script[1] == txscript.OP_DATA_20 && 212 script[22] == txscript.OP_EQUAL { 213 214 return script[2:22] 215 } 216 217 return nil 218 } 219 220 // isScriptHash returns whether or not the passed public key script is a 221 // standard pay-to-script-hash script. 222 func isScriptHash(script []byte) bool { 223 return extractScriptHash(script) != nil 224 } 225 226 // isPubKey returns whether or not the passed public key script is a standard 227 // pay-to-pubkey script that pays to a valid compressed or uncompressed public 228 // key along with the serialized pubkey it is paying to if it is. 229 // 230 // NOTE: This function ensures the public key is actually valid since the 231 // compression algorithm requires valid pubkeys. It does not support hybrid 232 // pubkeys. This means that even if the script has the correct form for a 233 // pay-to-pubkey script, this function will only return true when it is paying 234 // to a valid compressed or uncompressed pubkey. 235 func isPubKey(script []byte) (bool, []byte) { 236 // Pay-to-compressed-pubkey script. 237 if len(script) == 35 && script[0] == txscript.OP_DATA_33 && 238 script[34] == txscript.OP_CHECKSIG && (script[1] == 0x02 || 239 script[1] == 0x03) { 240 241 // Ensure the public key is valid. 242 serializedPubKey := script[1:34] 243 _, err := secp256k1.ParsePubKey(serializedPubKey) 244 if err == nil { 245 return true, serializedPubKey 246 } 247 } 248 249 // Pay-to-uncompressed-pubkey script. 250 if len(script) == 67 && script[0] == txscript.OP_DATA_65 && 251 script[66] == txscript.OP_CHECKSIG && script[1] == 0x04 { 252 253 // Ensure the public key is valid. 254 serializedPubKey := script[1:66] 255 _, err := secp256k1.ParsePubKey(serializedPubKey) 256 if err == nil { 257 return true, serializedPubKey 258 } 259 } 260 261 return false, nil 262 } 263 264 // compressedScriptSize returns the number of bytes the passed script would take 265 // when encoded with the domain specific compression algorithm described above. 266 func compressedScriptSize(scriptVersion uint16, pkScript []byte, 267 compressionVersion uint32) int { 268 // Pay-to-pubkey-hash or pay-to-script-hash script. 269 if isPubKeyHash(pkScript) || isScriptHash(pkScript) { 270 return 21 271 } 272 273 // Pay-to-pubkey (compressed or uncompressed) script. 274 if valid, _ := isPubKey(pkScript); valid { 275 return 33 276 } 277 278 // When none of the above special cases apply, encode the script as is 279 // preceded by the sum of its size and the number of special cases 280 // encoded as a variable length quantity. 281 return serializeSizeVLQ(uint64(len(pkScript)+numSpecialScripts)) + 282 len(pkScript) 283 } 284 285 // decodeCompressedScriptSize treats the passed serialized bytes as a compressed 286 // script, possibly followed by other data, and returns the number of bytes it 287 // occupies taking into account the special encoding of the script size by the 288 // domain specific compression algorithm described above. 289 func decodeCompressedScriptSize(serialized []byte, compressionVersion uint32) int { 290 scriptSize, bytesRead := deserializeVLQ(serialized) 291 if bytesRead == 0 { 292 return 0 293 } 294 295 switch scriptSize { 296 case cstPayToPubKeyHash: 297 return 21 298 299 case cstPayToScriptHash: 300 return 21 301 302 case cstPayToPubKeyCompEven, cstPayToPubKeyCompOdd, 303 cstPayToPubKeyUncompEven, cstPayToPubKeyUncompOdd: 304 return 33 305 } 306 307 scriptSize -= numSpecialScripts 308 scriptSize += uint64(bytesRead) 309 return int(scriptSize) 310 } 311 312 // putCompressedScript compresses the passed script according to the domain 313 // specific compression algorithm described above directly into the passed 314 // target byte slice. The target byte slice must be at least large enough to 315 // handle the number of bytes returned by the compressedScriptSize function or 316 // it will panic. 317 func putCompressedScript(target []byte, scriptVersion uint16, pkScript []byte, 318 compressionVersion uint32) int { 319 if len(target) == 0 { 320 target[0] = 0x00 321 return 1 322 } 323 324 // Pay-to-pubkey-hash script. 325 if hash := extractPubKeyHash(pkScript); hash != nil { 326 target[0] = cstPayToPubKeyHash 327 copy(target[1:21], hash) 328 return 21 329 } 330 331 // Pay-to-script-hash script. 332 if hash := extractScriptHash(pkScript); hash != nil { 333 target[0] = cstPayToScriptHash 334 copy(target[1:21], hash) 335 return 21 336 } 337 338 // Pay-to-pubkey (compressed or uncompressed) script. 339 if valid, serializedPubKey := isPubKey(pkScript); valid { 340 pubKeyFormat := serializedPubKey[0] 341 switch pubKeyFormat { 342 case 0x02, 0x03: 343 if pubKeyFormat == 0x02 { 344 target[0] = cstPayToPubKeyCompEven 345 } 346 if pubKeyFormat == 0x03 { 347 target[0] = cstPayToPubKeyCompOdd 348 } 349 copy(target[1:33], serializedPubKey[1:33]) 350 return 33 351 case 0x04: 352 // Encode the oddness of the serialized pubkey into the 353 // compressed script type. 354 target[0] = cstPayToPubKeyUncompEven 355 if (serializedPubKey[64] & 0x01) == 0x01 { 356 target[0] = cstPayToPubKeyUncompOdd 357 } 358 copy(target[1:33], serializedPubKey[1:33]) 359 return 33 360 } 361 } 362 363 // When none of the above special cases apply, encode the unmodified 364 // script preceded by the script version, the sum of its size and 365 // the number of special cases encoded as a variable length quantity. 366 encodedSize := uint64(len(pkScript) + numSpecialScripts) 367 vlqSizeLen := putVLQ(target, encodedSize) 368 copy(target[vlqSizeLen:], pkScript) 369 return vlqSizeLen + len(pkScript) 370 } 371 372 // decompressScript returns the original script obtained by decompressing the 373 // passed compressed script according to the domain specific compression 374 // algorithm described above. 375 // 376 // NOTE: The script parameter must already have been proven to be long enough 377 // to contain the number of bytes returned by decodeCompressedScriptSize or it 378 // will panic. This is acceptable since it is only an internal function. 379 func decompressScript(compressedPkScript []byte, 380 compressionVersion uint32) []byte { 381 // Empty scripts, specified by 0x00, are considered nil. 382 if len(compressedPkScript) == 0 { 383 return nil 384 } 385 386 // Decode the script size and examine it for the special cases. 387 encodedScriptSize, bytesRead := deserializeVLQ(compressedPkScript) 388 switch encodedScriptSize { 389 // Pay-to-pubkey-hash script. The resulting script is: 390 // <OP_DUP><OP_HASH160><20 byte hash><OP_EQUALVERIFY><OP_CHECKSIG> 391 case cstPayToPubKeyHash: 392 pkScript := make([]byte, 25) 393 pkScript[0] = txscript.OP_DUP 394 pkScript[1] = txscript.OP_HASH160 395 pkScript[2] = txscript.OP_DATA_20 396 copy(pkScript[3:], compressedPkScript[bytesRead:bytesRead+20]) 397 pkScript[23] = txscript.OP_EQUALVERIFY 398 pkScript[24] = txscript.OP_CHECKSIG 399 return pkScript 400 401 // Pay-to-script-hash script. The resulting script is: 402 // <OP_HASH160><20 byte script hash><OP_EQUAL> 403 case cstPayToScriptHash: 404 pkScript := make([]byte, 23) 405 pkScript[0] = txscript.OP_HASH160 406 pkScript[1] = txscript.OP_DATA_20 407 copy(pkScript[2:], compressedPkScript[bytesRead:bytesRead+20]) 408 pkScript[22] = txscript.OP_EQUAL 409 return pkScript 410 411 // Pay-to-compressed-pubkey script. The resulting script is: 412 // <OP_DATA_33><33 byte compressed pubkey><OP_CHECKSIG> 413 case cstPayToPubKeyCompEven, cstPayToPubKeyCompOdd: 414 pkScript := make([]byte, 35) 415 pkScript[0] = txscript.OP_DATA_33 416 oddness := byte(0x02) 417 if encodedScriptSize == cstPayToPubKeyCompOdd { 418 oddness = 0x03 419 } 420 pkScript[1] = oddness 421 copy(pkScript[2:], compressedPkScript[bytesRead:bytesRead+32]) 422 pkScript[34] = txscript.OP_CHECKSIG 423 return pkScript 424 425 // Pay-to-uncompressed-pubkey script. The resulting script is: 426 // <OP_DATA_65><65 byte uncompressed pubkey><OP_CHECKSIG> 427 case cstPayToPubKeyUncompEven, cstPayToPubKeyUncompOdd: 428 // Change the leading byte to the appropriate compressed pubkey 429 // identifier (0x02 or 0x03) so it can be decoded as a 430 // compressed pubkey. This really should never fail since the 431 // encoding ensures it is valid before compressing to this type. 432 compressedKey := make([]byte, 33) 433 oddness := byte(0x02) 434 if encodedScriptSize == cstPayToPubKeyUncompOdd { 435 oddness = 0x03 436 } 437 compressedKey[0] = oddness 438 copy(compressedKey[1:], compressedPkScript[1:]) 439 key, err := secp256k1.ParsePubKey(compressedKey) 440 if err != nil { 441 return nil 442 } 443 444 pkScript := make([]byte, 67) 445 pkScript[0] = txscript.OP_DATA_65 446 copy(pkScript[1:], key.SerializeUncompressed()) 447 pkScript[66] = txscript.OP_CHECKSIG 448 return pkScript 449 } 450 451 // When none of the special cases apply, the script was encoded using 452 // the general format, so reduce the script size by the number of 453 // special cases and return the unmodified script. 454 scriptSize := int(encodedScriptSize - numSpecialScripts) 455 pkScript := make([]byte, scriptSize) 456 copy(pkScript, compressedPkScript[bytesRead:bytesRead+scriptSize]) 457 return pkScript 458 } 459 460 // ----------------------------------------------------------------------------- 461 // In order to reduce the size of stored amounts, a domain specific compression 462 // algorithm is used which relies on there typically being a lot of zeroes at 463 // end of the amounts. The compression algorithm used here was obtained from 464 // Bitcoin Core, so all credits for the algorithm go to it. 465 // 466 // While this is simply exchanging one uint64 for another, the resulting value 467 // for typical amounts has a much smaller magnitude which results in fewer bytes 468 // when encoded as variable length quantity. For example, consider the amount 469 // of 0.1 DCR which is 10000000 atoms. Encoding 10000000 as a VarInt would take 470 // 4 bytes while encoding the compressed value of 8 as a VarInt only takes 1 byte. 471 // 472 // Essentially the compression is achieved by splitting the value into an 473 // exponent in the range [0-9] and a digit in the range [1-9], when possible, 474 // and encoding them in a way that can be decoded. More specifically, the 475 // encoding is as follows: 476 // - 0 is 0 477 // - Find the exponent, e, as the largest power of 10 that evenly divides the 478 // value up to a maximum of 9 479 // - When e < 9, the final digit can't be 0 so store it as d and remove it by 480 // dividing the value by 10 (call the result n). The encoded value is thus: 481 // 1 + 10*(9*n + d-1) + e 482 // - When e==9, the only thing known is the amount is not 0. The encoded value 483 // is thus: 484 // 1 + 10*(n-1) + e == 10 + 10*(n-1) 485 // 486 // Example encodings: 487 // (The numbers in parenthesis are the number of bytes when serialized as a VarInt) 488 // 0 (1) -> 0 (1) * 0.00000000 BTC 489 // 1000 (2) -> 4 (1) * 0.00001000 BTC 490 // 10000 (2) -> 5 (1) * 0.00010000 BTC 491 // 12345678 (4) -> 111111101(4) * 0.12345678 BTC 492 // 50000000 (4) -> 47 (1) * 0.50000000 BTC 493 // 100000000 (4) -> 9 (1) * 1.00000000 BTC 494 // 500000000 (5) -> 49 (1) * 5.00000000 BTC 495 // 1000000000 (5) -> 10 (1) * 10.00000000 BTC 496 // ----------------------------------------------------------------------------- 497 498 // compressTxOutAmount compresses the passed amount according to the domain 499 // specific compression algorithm described above. 500 func compressTxOutAmount(amount uint64) uint64 { 501 // No need to do any work if it's zero. 502 if amount == 0 { 503 return 0 504 } 505 506 // Find the largest power of 10 (max of 9) that evenly divides the 507 // value. 508 exponent := uint64(0) 509 for amount%10 == 0 && exponent < 9 { 510 amount /= 10 511 exponent++ 512 } 513 514 // The compressed result for exponents less than 9 is: 515 // 1 + 10*(9*n + d-1) + e 516 if exponent < 9 { 517 lastDigit := amount % 10 518 amount /= 10 519 return 1 + 10*(9*amount+lastDigit-1) + exponent 520 } 521 522 // The compressed result for an exponent of 9 is: 523 // 1 + 10*(n-1) + e == 10 + 10*(n-1) 524 return 10 + 10*(amount-1) 525 } 526 527 // decompressTxOutAmount returns the original amount the passed compressed 528 // amount represents according to the domain specific compression algorithm 529 // described above. 530 func decompressTxOutAmount(amount uint64) uint64 { 531 // No need to do any work if it's zero. 532 if amount == 0 { 533 return 0 534 } 535 536 // The decompressed amount is either of the following two equations: 537 // x = 1 + 10*(9*n + d - 1) + e 538 // x = 1 + 10*(n - 1) + 9 539 amount-- 540 541 // The decompressed amount is now one of the following two equations: 542 // x = 10*(9*n + d - 1) + e 543 // x = 10*(n - 1) + 9 544 exponent := amount % 10 545 amount /= 10 546 547 // The decompressed amount is now one of the following two equations: 548 // x = 9*n + d - 1 | where e < 9 549 // x = n - 1 | where e = 9 550 var n uint64 551 if exponent < 9 { 552 lastDigit := amount%9 + 1 553 amount /= 9 554 n = amount*10 + lastDigit 555 } else { 556 n = amount + 1 557 } 558 559 // Apply the exponent. 560 for ; exponent > 0; exponent-- { 561 n *= 10 562 } 563 564 return n 565 } 566 567 // ----------------------------------------------------------------------------- 568 // Compressed transaction outputs for UTXOS consist of an amount and a public 569 // key script both compressed using the domain specific compression algorithms 570 // previously described. 571 // 572 // The serialized format is: 573 // 574 // <compressed amount><compressed script> 575 // 576 // Field Type Size 577 // compressed amount VLQ variable 578 // compressed script []byte variable 579 // ----------------------------------------------------------------------------- 580 581 // compressedTxOutSize returns the number of bytes the passed transaction output 582 // fields would take when encoded with the format described above. The 583 // preCompressed flag indicates the provided amount and script are already 584 // compressed. This is useful since loaded utxo entries are not decompressed 585 // until the output is accessed. 586 func compressedTxOutSize(amount uint64, scriptVersion uint16, pkScript []byte, 587 compressionVersion uint32, preCompressed bool, hasAmount bool) int { 588 scriptVersionSize := serializeSizeVLQ(uint64(scriptVersion)) 589 if preCompressed && !hasAmount { 590 return scriptVersionSize + len(pkScript) 591 } 592 if preCompressed && hasAmount { 593 return scriptVersionSize + serializeSizeVLQ(compressTxOutAmount(amount)) + 594 len(pkScript) 595 } 596 if !preCompressed && !hasAmount { 597 return scriptVersionSize + compressedScriptSize(scriptVersion, 598 pkScript, compressionVersion) 599 } 600 601 // if !preCompressed && hasAmount 602 return scriptVersionSize + serializeSizeVLQ(compressTxOutAmount(amount)) + 603 compressedScriptSize(scriptVersion, pkScript, compressionVersion) 604 } 605 606 // putCompressedTxOut potentially compresses the passed amount and script 607 // according to their domain specific compression algorithms and encodes them 608 // directly into the passed target byte slice with the format described above. 609 // The preCompressed flag indicates the provided amount and script are already 610 // compressed in which case the values are not modified. This is useful since 611 // loaded utxo entries are not decompressed until the output is accessed. The 612 // target byte slice must be at least large enough to handle the number of bytes 613 // returned by the compressedTxOutSize function or it will panic. 614 func putCompressedTxOut(target []byte, amount uint64, scriptVersion uint16, 615 pkScript []byte, compressionVersion uint32, preCompressed bool, 616 hasAmount bool) int { 617 if preCompressed && hasAmount { 618 offset := putVLQ(target, compressTxOutAmount(amount)) 619 offset += putVLQ(target[offset:], uint64(scriptVersion)) 620 copy(target[offset:], pkScript) 621 return offset + len(pkScript) 622 } 623 if preCompressed && !hasAmount { 624 offset := putVLQ(target, uint64(scriptVersion)) 625 copy(target[offset:], pkScript) 626 return offset + len(pkScript) 627 } 628 if !preCompressed && !hasAmount { 629 offset := putVLQ(target, uint64(scriptVersion)) 630 offset += putCompressedScript(target[offset:], scriptVersion, pkScript, 631 compressionVersion) 632 return offset 633 } 634 635 // if !preCompressed && hasAmount 636 offset := putVLQ(target, compressTxOutAmount(amount)) 637 offset += putVLQ(target[offset:], uint64(scriptVersion)) 638 offset += putCompressedScript(target[offset:], scriptVersion, pkScript, 639 compressionVersion) 640 return offset 641 } 642 643 // decodeCompressedTxOut decodes the passed compressed txout, possibly followed 644 // by other data, into its compressed amount and compressed script and returns 645 // them along with the number of bytes they occupied. 646 func decodeCompressedTxOut(serialized []byte, compressionVersion uint32, 647 hasAmount bool) (int64, uint16, []byte, int, error) { 648 var amount int64 649 var bytesRead int 650 var offset int 651 if hasAmount { 652 // Deserialize the compressed amount and ensure there are bytes 653 // remaining for the compressed script. 654 var compressedAmount uint64 655 compressedAmount, bytesRead = deserializeVLQ(serialized) 656 if bytesRead >= len(serialized) { 657 return 0, 0, nil, bytesRead, errDeserialize("unexpected end of " + 658 "data after compressed amount") 659 } 660 amount = int64(decompressTxOutAmount(compressedAmount)) 661 offset += bytesRead 662 } 663 664 // Decode the script version. 665 var scriptVersion uint64 666 scriptVersion, bytesRead = deserializeVLQ(serialized[offset:]) 667 offset += bytesRead 668 669 // Decode the compressed script size and ensure there are enough bytes 670 // left in the slice for it. 671 scriptSize := decodeCompressedScriptSize(serialized[offset:], 672 compressionVersion) 673 if scriptSize < 0 { 674 return 0, 0, nil, offset, errDeserialize("negative script size") 675 } 676 if len(serialized[offset:]) < scriptSize { 677 return 0, 0, nil, offset, errDeserialize(fmt.Sprintf("unexpected end of "+ 678 "data after script size (got %v, need %v)", len(serialized[offset:]), 679 scriptSize)) 680 } 681 682 // Make a copy of the compressed script so the original serialized data 683 // can be released as soon as possible. 684 compressedScript := make([]byte, scriptSize) 685 copy(compressedScript, serialized[offset:offset+scriptSize]) 686 687 return amount, uint16(scriptVersion), compressedScript, 688 offset + scriptSize, nil 689 } 690 691 // ----------------------------------------------------------------------------- 692 // Decred specific transaction encoding flags 693 // 694 // Details about a transaction needed to determine how it may be spent 695 // according to consensus rules are given by these flags. 696 // 697 // The following details are encoded into a single byte, where the index 698 // of the bit is given in zeroeth order: 699 // 0: Is coinbase 700 // 1: Has an expiry 701 // 2-3: Transaction type 702 // 4: Fully spent 703 // 5-7: Unused 704 // 705 // 0, 1, and 4 are bit flags, while the transaction type is encoded with a bitmask 706 // and used to describe the underlying int. 707 // 708 // The fully spent flag should always come as the *last* flag (highest bit index) 709 // in this data type should flags be updated to include more rules in the future, 710 // such as rules governing new script OP codes. This ensures that we may still use 711 // these flags in the UTX serialized data without consequence, where the last flag 712 // indicating fully spent will always be zeroed. 713 // 714 // ----------------------------------------------------------------------------- 715 716 const ( 717 // txTypeBitmask describes the bitmask that yields the 3rd and 4th bits 718 // from the flags byte. 719 txTypeBitmask = 0x0c 720 721 // txTypeShift is the number of bits to shift falgs to the right to yield the 722 // correct integer value after applying the bitmask with AND. 723 txTypeShift = 2 724 ) 725 726 // encodeFlags encodes transaction flags into a single byte. 727 func encodeFlags(isCoinBase bool, hasExpiry bool, txType stake.TxType, fullySpent bool) byte { 728 b := uint8(txType) 729 b <<= txTypeShift 730 731 if isCoinBase { 732 b |= 0x01 // Set bit 0 733 } 734 if hasExpiry { 735 b |= 0x02 // Set bit 1 736 } 737 if fullySpent { 738 b |= 0x10 // Set bit 4 739 } 740 741 return b 742 } 743 744 // decodeFlags decodes transaction flags from a single byte into their respective 745 // data types. 746 func decodeFlags(b byte) (bool, bool, stake.TxType, bool) { 747 isCoinBase := b&0x01 != 0 748 hasExpiry := b&(1<<1) != 0 749 fullySpent := b&(1<<4) != 0 750 txType := stake.TxType((b & txTypeBitmask) >> txTypeShift) 751 752 return isCoinBase, hasExpiry, txType, fullySpent 753 } 754 755 // decodeFlagsFullySpent decodes whether or not a transaction was fully spent. 756 func decodeFlagsFullySpent(b byte) bool { 757 return b&(1<<4) != 0 758 }