[NOD-848] optimize utxo diffs serialize allocations (#666)

* [NOD-848] Optimize allocations when serializing UTXO diffs

* [NOD-848] Use same UTXO serialization everywhere, and use compression as well

* [NOD-848] Fix usage of wrong buffer

* [NOD-848] Fix tests

* [NOD-848] Fix wire tests

* [NOD-848] Fix tests

* [NOD-848] Remove VLQ

* [NOD-848] Fix comments

* [NOD-848] Add varint for big endian encoding

* [NOD-848] In TestVarIntWire, assume the expected decoded value is the same as the serialization input

* [NOD-848] Serialize outpoint index with big endian varint

* [NOD-848] Remove p2pk from compression support

* [NOD-848] Fix comments

* [NOD-848] Remove p2pk from decompression support

* [NOD-848] Make entry compression optional

* [NOD-848] Fix tests

* [NOD-848] Fix comments and var names

* [NOD-848] Remove UTXO compression

* [NOD-848] Fix tests

* [NOD-848] Remove big endian varint

* [NOD-848] Fix comments

* [NOD-848] Rename ReadVarIntLittleEndian->ReadVarInt and fix WriteVarInt comment

* [NOD-848] Add outpointIndexByteOrder variable

* [NOD-848] Remove redundant comment

* [NOD-848] Fix outpointMaxSerializeSize to the correct value

* [NOD-848] Move subBuffer to utils
This commit is contained in:
Ori Newman 2020-03-24 16:44:41 +02:00 committed by GitHub
parent 3c67215e76
commit d3b1953deb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
40 changed files with 495 additions and 1532 deletions

View File

@ -73,15 +73,8 @@ func loadUTXOSet(filename string) (UTXOSet, error) {
return nil, err return nil, err
} }
// Serialized utxo entry. // Deserialize the UTXO entry and add it to the UTXO set.
serialized := make([]byte, numBytes) entry, err := deserializeUTXOEntry(r)
_, err = io.ReadAtLeast(r, serialized, int(numBytes))
if err != nil {
return nil, err
}
// Deserialize it and add it to the view.
entry, err := deserializeUTXOEntry(serialized)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -1,584 +0,0 @@
// Copyright (c) 2015-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"github.com/kaspanet/kaspad/ecc"
"github.com/kaspanet/kaspad/txscript"
)
// -----------------------------------------------------------------------------
// A variable length quantity (VLQ) is an encoding that uses an arbitrary number
// of binary octets to represent an arbitrarily large integer. The scheme
// employs a most significant byte (MSB) base-128 encoding where the high bit in
// each byte indicates whether or not the byte is the final one. In addition,
// to ensure there are no redundant encodings, an offset is subtracted every
// time a group of 7 bits is shifted out. Therefore each integer can be
// represented in exactly one way, and each representation stands for exactly
// one integer.
//
// Another nice property of this encoding is that it provides a compact
// representation of values that are typically used to indicate sizes. For
// example, the values 0 - 127 are represented with a single byte, 128 - 16511
// with two bytes, and 16512 - 2113663 with three bytes.
//
// While the encoding allows arbitrarily large integers, it is artificially
// limited in this code to an unsigned 64-bit integer for efficiency purposes.
//
// Example encodings:
// 0 -> [0x00]
// 127 -> [0x7f] * Max 1-byte value
// 128 -> [0x80 0x00]
// 129 -> [0x80 0x01]
// 255 -> [0x80 0x7f]
// 256 -> [0x81 0x00]
// 16511 -> [0xff 0x7f] * Max 2-byte value
// 16512 -> [0x80 0x80 0x00]
// 32895 -> [0x80 0xff 0x7f]
// 2113663 -> [0xff 0xff 0x7f] * Max 3-byte value
// 270549119 -> [0xff 0xff 0xff 0x7f] * Max 4-byte value
// 2^64-1 -> [0x80 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0xfe 0x7f]
//
// References:
// https://en.wikipedia.org/wiki/Variable-length_quantity
// http://www.codecodex.com/wiki/Variable-Length_Integers
// -----------------------------------------------------------------------------
// serializeSizeVLQ returns the number of bytes it would take to serialize the
// passed number as a variable-length quantity according to the format described
// above.
func serializeSizeVLQ(n uint64) int {
size := 1
for ; n > 0x7f; n = (n >> 7) - 1 {
size++
}
return size
}
// putVLQ serializes the provided number to a variable-length quantity according
// to the format described above and returns the number of bytes of the encoded
// value. The result is placed directly into the passed byte slice which must
// be at least large enough to handle the number of bytes returned by the
// serializeSizeVLQ function or it will panic.
func putVLQ(target []byte, n uint64) int {
offset := 0
for ; ; offset++ {
// The high bit is set when another byte follows.
highBitMask := byte(0x80)
if offset == 0 {
highBitMask = 0x00
}
target[offset] = byte(n&0x7f) | highBitMask
if n <= 0x7f {
break
}
n = (n >> 7) - 1
}
// Reverse the bytes so it is MSB-encoded.
for i, j := 0, offset; i < j; i, j = i+1, j-1 {
target[i], target[j] = target[j], target[i]
}
return offset + 1
}
// deserializeVLQ deserializes the provided variable-length quantity according
// to the format described above. It also returns the number of bytes
// deserialized.
func deserializeVLQ(serialized []byte) (uint64, int) {
var n uint64
var size int
for _, val := range serialized {
size++
n = (n << 7) | uint64(val&0x7f)
if val&0x80 != 0x80 {
break
}
n++
}
return n, size
}
// -----------------------------------------------------------------------------
// In order to reduce the size of stored scripts, a domain specific compression
// algorithm is used which recognizes standard scripts and stores them using
// less bytes than the original script.
//
// The general serialized format is:
//
// <script size or type><script data>
//
// Field Type Size
// script size or type VLQ variable
// script data []byte variable
//
// The specific serialized format for each recognized standard script is:
//
// - Pay-to-pubkey-hash: (21 bytes) - <0><20-byte pubkey hash>
// - Pay-to-script-hash: (21 bytes) - <1><20-byte script hash>
// - Pay-to-pubkey**: (33 bytes) - <2, 3, 4, or 5><32-byte pubkey X value>
// 2, 3 = compressed pubkey with bit 0 specifying the y coordinate to use
// 4, 5 = uncompressed pubkey with bit 0 specifying the y coordinate to use
// ** Only valid public keys starting with 0x02, 0x03, and 0x04 are supported.
//
// Any scripts which are not recognized as one of the aforementioned standard
// scripts are encoded using the general serialized format and encode the script
// size as the sum of the actual size of the script and the number of special
// cases.
// -----------------------------------------------------------------------------
// The following constants specify the special constants used to identify a
// special script type in the domain-specific compressed script encoding.
//
// NOTE: This section specifically does not use iota since these values are
// serialized and must be stable for long-term storage.
const (
// cstPayToPubKeyHash identifies a compressed pay-to-pubkey-hash script.
cstPayToPubKeyHash = 0
// cstPayToScriptHash identifies a compressed pay-to-script-hash script.
cstPayToScriptHash = 1
// cstPayToPubKeyComp2 identifies a compressed pay-to-pubkey script to
// a compressed pubkey. Bit 0 specifies which y-coordinate to use
// to reconstruct the full uncompressed pubkey.
cstPayToPubKeyComp2 = 2
// cstPayToPubKeyComp3 identifies a compressed pay-to-pubkey script to
// a compressed pubkey. Bit 0 specifies which y-coordinate to use
// to reconstruct the full uncompressed pubkey.
cstPayToPubKeyComp3 = 3
// cstPayToPubKeyUncomp4 identifies a compressed pay-to-pubkey script to
// an uncompressed pubkey. Bit 0 specifies which y-coordinate to use
// to reconstruct the full uncompressed pubkey.
cstPayToPubKeyUncomp4 = 4
// cstPayToPubKeyUncomp5 identifies a compressed pay-to-pubkey script to
// an uncompressed pubkey. Bit 0 specifies which y-coordinate to use
// to reconstruct the full uncompressed pubkey.
cstPayToPubKeyUncomp5 = 5
// numSpecialScripts is the number of special scripts recognized by the
// domain-specific script compression algorithm.
numSpecialScripts = 6
)
// isPubKeyHash returns whether or not the passed public key script is a
// standard pay-to-pubkey-hash script along with the pubkey hash it is paying to
// if it is.
func isPubKeyHash(script []byte) (bool, []byte) {
if len(script) == 25 && script[0] == txscript.OpDup &&
script[1] == txscript.OpHash160 &&
script[2] == txscript.OpData20 &&
script[23] == txscript.OpEqualVerify &&
script[24] == txscript.OpCheckSig {
return true, script[3:23]
}
return false, nil
}
// isScriptHash returns whether or not the passed public key script is a
// standard pay-to-script-hash script along with the script hash it is paying to
// if it is.
func isScriptHash(script []byte) (bool, []byte) {
if len(script) == 23 && script[0] == txscript.OpHash160 &&
script[1] == txscript.OpData20 &&
script[22] == txscript.OpEqual {
return true, script[2:22]
}
return false, nil
}
// isPubKey returns whether or not the passed public key script is a standard
// pay-to-pubkey script that pays to a valid compressed or uncompressed public
// key along with the serialized pubkey it is paying to if it is.
//
// NOTE: This function ensures the public key is actually valid since the
// compression algorithm requires valid pubkeys. It does not support hybrid
// pubkeys. This means that even if the script has the correct form for a
// pay-to-pubkey script, this function will only return true when it is paying
// to a valid compressed or uncompressed pubkey.
func isPubKey(script []byte) (bool, []byte) {
// Pay-to-compressed-pubkey script.
if len(script) == 35 && script[0] == txscript.OpData33 &&
script[34] == txscript.OpCheckSig && (script[1] == 0x02 ||
script[1] == 0x03) {
// Ensure the public key is valid.
serializedPubKey := script[1:34]
_, err := ecc.ParsePubKey(serializedPubKey, ecc.S256())
if err == nil {
return true, serializedPubKey
}
}
// Pay-to-uncompressed-pubkey script.
if len(script) == 67 && script[0] == txscript.OpData65 &&
script[66] == txscript.OpCheckSig && script[1] == 0x04 {
// Ensure the public key is valid.
serializedPubKey := script[1:66]
_, err := ecc.ParsePubKey(serializedPubKey, ecc.S256())
if err == nil {
return true, serializedPubKey
}
}
return false, nil
}
// compressedScriptSize returns the number of bytes the passed script would take
// when encoded with the domain specific compression algorithm described above.
func compressedScriptSize(scriptPubKey []byte) int {
// Pay-to-pubkey-hash script.
if valid, _ := isPubKeyHash(scriptPubKey); valid {
return 21
}
// Pay-to-script-hash script.
if valid, _ := isScriptHash(scriptPubKey); valid {
return 21
}
// Pay-to-pubkey (compressed or uncompressed) script.
if valid, _ := isPubKey(scriptPubKey); valid {
return 33
}
// When none of the above special cases apply, encode the script as is
// preceded by the sum of its size and the number of special cases
// encoded as a variable length quantity.
return serializeSizeVLQ(uint64(len(scriptPubKey)+numSpecialScripts)) +
len(scriptPubKey)
}
// decodeCompressedScriptSize treats the passed serialized bytes as a compressed
// script, possibly followed by other data, and returns the number of bytes it
// occupies taking into account the special encoding of the script size by the
// domain specific compression algorithm described above.
func decodeCompressedScriptSize(serialized []byte) int {
scriptSize, bytesRead := deserializeVLQ(serialized)
if bytesRead == 0 {
return 0
}
switch scriptSize {
case cstPayToPubKeyHash:
return 21
case cstPayToScriptHash:
return 21
case cstPayToPubKeyComp2, cstPayToPubKeyComp3, cstPayToPubKeyUncomp4,
cstPayToPubKeyUncomp5:
return 33
}
scriptSize -= numSpecialScripts
scriptSize += uint64(bytesRead)
return int(scriptSize)
}
// putCompressedScript compresses the passed script according to the domain
// specific compression algorithm described above directly into the passed
// target byte slice. The target byte slice must be at least large enough to
// handle the number of bytes returned by the compressedScriptSize function or
// it will panic.
func putCompressedScript(target, scriptPubKey []byte) int {
// Pay-to-pubkey-hash script.
if valid, hash := isPubKeyHash(scriptPubKey); valid {
target[0] = cstPayToPubKeyHash
copy(target[1:21], hash)
return 21
}
// Pay-to-script-hash script.
if valid, hash := isScriptHash(scriptPubKey); valid {
target[0] = cstPayToScriptHash
copy(target[1:21], hash)
return 21
}
// Pay-to-pubkey (compressed or uncompressed) script.
if valid, serializedPubKey := isPubKey(scriptPubKey); valid {
pubKeyFormat := serializedPubKey[0]
switch pubKeyFormat {
case 0x02, 0x03:
target[0] = pubKeyFormat
copy(target[1:33], serializedPubKey[1:33])
return 33
case 0x04:
// Encode the oddness of the serialized pubkey into the
// compressed script type.
target[0] = pubKeyFormat | (serializedPubKey[64] & 0x01)
copy(target[1:33], serializedPubKey[1:33])
return 33
}
}
// When none of the above special cases apply, encode the unmodified
// script preceded by the sum of its size and the number of special
// cases encoded as a variable length quantity.
encodedSize := uint64(len(scriptPubKey) + numSpecialScripts)
vlqSizeLen := putVLQ(target, encodedSize)
copy(target[vlqSizeLen:], scriptPubKey)
return vlqSizeLen + len(scriptPubKey)
}
// decompressScript returns the original script obtained by decompressing the
// passed compressed script according to the domain specific compression
// algorithm described above.
//
// NOTE: The script parameter must already have been proven to be long enough
// to contain the number of bytes returned by decodeCompressedScriptSize or it
// will panic. This is acceptable since it is only an internal function.
func decompressScript(compressedScriptPubKey []byte) []byte {
// In practice this function will not be called with a zero-length or
// nil script since the nil script encoding includes the length, however
// the code below assumes the length exists, so just return nil now if
// the function ever ends up being called with a nil script in the
// future.
if len(compressedScriptPubKey) == 0 {
return nil
}
// Decode the script size and examine it for the special cases.
encodedScriptSize, bytesRead := deserializeVLQ(compressedScriptPubKey)
switch encodedScriptSize {
// Pay-to-pubkey-hash script. The resulting script is:
// <OP_DUP><OP_HASH160><20 byte hash><OP_EQUALVERIFY><OP_CHECKSIG>
case cstPayToPubKeyHash:
scriptPubKey := make([]byte, 25)
scriptPubKey[0] = txscript.OpDup
scriptPubKey[1] = txscript.OpHash160
scriptPubKey[2] = txscript.OpData20
copy(scriptPubKey[3:], compressedScriptPubKey[bytesRead:bytesRead+20])
scriptPubKey[23] = txscript.OpEqualVerify
scriptPubKey[24] = txscript.OpCheckSig
return scriptPubKey
// Pay-to-script-hash script. The resulting script is:
// <OP_HASH160><20 byte script hash><OP_EQUAL>
case cstPayToScriptHash:
scriptPubKey := make([]byte, 23)
scriptPubKey[0] = txscript.OpHash160
scriptPubKey[1] = txscript.OpData20
copy(scriptPubKey[2:], compressedScriptPubKey[bytesRead:bytesRead+20])
scriptPubKey[22] = txscript.OpEqual
return scriptPubKey
// Pay-to-compressed-pubkey script. The resulting script is:
// <OP_DATA_33><33 byte compressed pubkey><OP_CHECKSIG>
case cstPayToPubKeyComp2, cstPayToPubKeyComp3:
scriptPubKey := make([]byte, 35)
scriptPubKey[0] = txscript.OpData33
scriptPubKey[1] = byte(encodedScriptSize)
copy(scriptPubKey[2:], compressedScriptPubKey[bytesRead:bytesRead+32])
scriptPubKey[34] = txscript.OpCheckSig
return scriptPubKey
// Pay-to-uncompressed-pubkey script. The resulting script is:
// <OP_DATA_65><65 byte uncompressed pubkey><OP_CHECKSIG>
case cstPayToPubKeyUncomp4, cstPayToPubKeyUncomp5:
// Change the leading byte to the appropriate compressed pubkey
// identifier (0x02 or 0x03) so it can be decoded as a
// compressed pubkey. This really should never fail since the
// encoding ensures it is valid before compressing to this type.
compressedKey := make([]byte, 33)
compressedKey[0] = byte(encodedScriptSize - 2)
copy(compressedKey[1:], compressedScriptPubKey[1:])
key, err := ecc.ParsePubKey(compressedKey, ecc.S256())
if err != nil {
return nil
}
scriptPubKey := make([]byte, 67)
scriptPubKey[0] = txscript.OpData65
copy(scriptPubKey[1:], key.SerializeUncompressed())
scriptPubKey[66] = txscript.OpCheckSig
return scriptPubKey
}
// When none of the special cases apply, the script was encoded using
// the general format, so reduce the script size by the number of
// special cases and return the unmodified script.
scriptSize := int(encodedScriptSize - numSpecialScripts)
scriptPubKey := make([]byte, scriptSize)
copy(scriptPubKey, compressedScriptPubKey[bytesRead:bytesRead+scriptSize])
return scriptPubKey
}
// -----------------------------------------------------------------------------
// In order to reduce the size of stored amounts, a domain specific compression
// algorithm is used which relies on there typically being a lot of zeroes at
// end of the amounts.
//
// While this is simply exchanging one uint64 for another, the resulting value
// for typical amounts has a much smaller magnitude which results in fewer bytes
// when encoded as variable length quantity. For example, consider the amount
// of 0.1 KAS which is 10000000 sompi. Encoding 10000000 as a VLQ would take
// 4 bytes while encoding the compressed value of 8 as a VLQ only takes 1 byte.
//
// Essentially the compression is achieved by splitting the value into an
// exponent in the range [0-9] and a digit in the range [1-9], when possible,
// and encoding them in a way that can be decoded. More specifically, the
// encoding is as follows:
// - 0 is 0
// - Find the exponent, e, as the largest power of 10 that evenly divides the
// value up to a maximum of 9
// - When e < 9, the final digit can't be 0 so store it as d and remove it by
// dividing the value by 10 (call the result n). The encoded value is thus:
// 1 + 10*(9*n + d-1) + e
// - When e==9, the only thing known is the amount is not 0. The encoded value
// is thus:
// 1 + 10*(n-1) + e == 10 + 10*(n-1)
//
// Example encodings:
// (The numbers in parenthesis are the number of bytes when serialized as a VLQ)
// 0 (1) -> 0 (1) * 0.00000000 KAS
// 1000 (2) -> 4 (1) * 0.00001000 KAS
// 10000 (2) -> 5 (1) * 0.00010000 KAS
// 12345678 (4) -> 111111101(4) * 0.12345678 KAS
// 50000000 (4) -> 47 (1) * 0.50000000 KAS
// 100000000 (4) -> 9 (1) * 1.00000000 KAS
// 500000000 (5) -> 49 (1) * 5.00000000 KAS
// 1000000000 (5) -> 10 (1) * 10.00000000 KAS
// -----------------------------------------------------------------------------
// compressTxOutAmount compresses the passed amount according to the domain
// specific compression algorithm described above.
func compressTxOutAmount(amount uint64) uint64 {
// No need to do any work if it's zero.
if amount == 0 {
return 0
}
// Find the largest power of 10 (max of 9) that evenly divides the
// value.
exponent := uint64(0)
for amount%10 == 0 && exponent < 9 {
amount /= 10
exponent++
}
// The compressed result for exponents less than 9 is:
// 1 + 10*(9*n + d-1) + e
if exponent < 9 {
lastDigit := amount % 10
amount /= 10
return 1 + 10*(9*amount+lastDigit-1) + exponent
}
// The compressed result for an exponent of 9 is:
// 1 + 10*(n-1) + e == 10 + 10*(n-1)
return 10 + 10*(amount-1)
}
// decompressTxOutAmount returns the original amount the passed compressed
// amount represents according to the domain specific compression algorithm
// described above.
func decompressTxOutAmount(amount uint64) uint64 {
// No need to do any work if it's zero.
if amount == 0 {
return 0
}
// The decompressed amount is either of the following two equations:
// x = 1 + 10*(9*n + d - 1) + e
// x = 1 + 10*(n - 1) + 9
amount--
// The decompressed amount is now one of the following two equations:
// x = 10*(9*n + d - 1) + e
// x = 10*(n - 1) + 9
exponent := amount % 10
amount /= 10
// The decompressed amount is now one of the following two equations:
// x = 9*n + d - 1 | where e < 9
// x = n - 1 | where e = 9
n := uint64(0)
if exponent < 9 {
lastDigit := amount%9 + 1
amount /= 9
n = amount*10 + lastDigit
} else {
n = amount + 1
}
// Apply the exponent.
for ; exponent > 0; exponent-- {
n *= 10
}
return n
}
// -----------------------------------------------------------------------------
// Compressed transaction outputs consist of an amount and a public key script
// both compressed using the domain specific compression algorithms previously
// described.
//
// The serialized format is:
//
// <compressed amount><compressed script>
//
// Field Type Size
// compressed amount VLQ variable
// compressed script []byte variable
// -----------------------------------------------------------------------------
// compressedTxOutSize returns the number of bytes the passed transaction output
// fields would take when encoded with the format described above.
func compressedTxOutSize(amount uint64, scriptPubKey []byte) int {
return serializeSizeVLQ(compressTxOutAmount(amount)) +
compressedScriptSize(scriptPubKey)
}
// putCompressedTxOut compresses the passed amount and script according to their
// domain specific compression algorithms and encodes them directly into the
// passed target byte slice with the format described above. The target byte
// slice must be at least large enough to handle the number of bytes returned by
// the compressedTxOutSize function or it will panic.
func putCompressedTxOut(target []byte, amount uint64, scriptPubKey []byte) int {
offset := putVLQ(target, compressTxOutAmount(amount))
offset += putCompressedScript(target[offset:], scriptPubKey)
return offset
}
// decodeCompressedTxOut decodes the passed compressed txout, possibly followed
// by other data, into its uncompressed amount and script and returns them along
// with the number of bytes they occupied prior to decompression.
func decodeCompressedTxOut(serialized []byte) (uint64, []byte, int, error) {
// Deserialize the compressed amount and ensure there are bytes
// remaining for the compressed script.
compressedAmount, bytesRead := deserializeVLQ(serialized)
if bytesRead >= len(serialized) {
return 0, nil, bytesRead, errDeserialize("unexpected end of " +
"data after compressed amount")
}
// Decode the compressed script size and ensure there are enough bytes
// left in the slice for it.
scriptSize := decodeCompressedScriptSize(serialized[bytesRead:])
if len(serialized[bytesRead:]) < scriptSize {
return 0, nil, bytesRead, errDeserialize("unexpected end of " +
"data after script size")
}
// Decompress and return the amount and script.
amount := decompressTxOutAmount(compressedAmount)
script := decompressScript(serialized[bytesRead : bytesRead+scriptSize])
return amount, script, bytesRead + scriptSize, nil
}

View File

@ -1,436 +0,0 @@
// Copyright (c) 2015-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package blockdag
import (
"bytes"
"encoding/hex"
"testing"
)
// hexToBytes converts the passed hex string into bytes and will panic if there
// is an error. This is only provided for the hard-coded constants so errors in
// the source code can be detected. It will only (and must only) be called with
// hard-coded values.
func hexToBytes(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic("invalid hex in source file: " + s)
}
return b
}
// TestVLQ ensures the variable length quantity serialization, deserialization,
// and size calculation works as expected.
func TestVLQ(t *testing.T) {
t.Parallel()
tests := []struct {
val uint64
serialized []byte
}{
{0, hexToBytes("00")},
{1, hexToBytes("01")},
{127, hexToBytes("7f")},
{128, hexToBytes("8000")},
{129, hexToBytes("8001")},
{255, hexToBytes("807f")},
{256, hexToBytes("8100")},
{16383, hexToBytes("fe7f")},
{16384, hexToBytes("ff00")},
{16511, hexToBytes("ff7f")}, // Max 2-byte value
{16512, hexToBytes("808000")},
{16513, hexToBytes("808001")},
{16639, hexToBytes("80807f")},
{32895, hexToBytes("80ff7f")},
{2113663, hexToBytes("ffff7f")}, // Max 3-byte value
{2113664, hexToBytes("80808000")},
{270549119, hexToBytes("ffffff7f")}, // Max 4-byte value
{270549120, hexToBytes("8080808000")},
{2147483647, hexToBytes("86fefefe7f")},
{2147483648, hexToBytes("86fefeff00")},
{4294967295, hexToBytes("8efefefe7f")}, // Max uint32, 5 bytes
// Max uint64, 10 bytes
{18446744073709551615, hexToBytes("80fefefefefefefefe7f")},
}
for _, test := range tests {
// Ensure the function to calculate the serialized size without
// actually serializing the value is calculated properly.
gotSize := serializeSizeVLQ(test.val)
if gotSize != len(test.serialized) {
t.Errorf("serializeSizeVLQ: did not get expected size "+
"for %d - got %d, want %d", test.val, gotSize,
len(test.serialized))
continue
}
// Ensure the value serializes to the expected bytes.
gotBytes := make([]byte, gotSize)
gotBytesWritten := putVLQ(gotBytes, test.val)
if !bytes.Equal(gotBytes, test.serialized) {
t.Errorf("putVLQUnchecked: did not get expected bytes "+
"for %d - got %x, want %x", test.val, gotBytes,
test.serialized)
continue
}
if gotBytesWritten != len(test.serialized) {
t.Errorf("putVLQUnchecked: did not get expected number "+
"of bytes written for %d - got %d, want %d",
test.val, gotBytesWritten, len(test.serialized))
continue
}
// Ensure the serialized bytes deserialize to the expected
// value.
gotVal, gotBytesRead := deserializeVLQ(test.serialized)
if gotVal != test.val {
t.Errorf("deserializeVLQ: did not get expected value "+
"for %x - got %d, want %d", test.serialized,
gotVal, test.val)
continue
}
if gotBytesRead != len(test.serialized) {
t.Errorf("deserializeVLQ: did not get expected number "+
"of bytes read for %d - got %d, want %d",
test.serialized, gotBytesRead,
len(test.serialized))
continue
}
}
}
// TestScriptCompression ensures the domain-specific script compression and
// decompression works as expected.
func TestScriptCompression(t *testing.T) {
t.Parallel()
tests := []struct {
name string
uncompressed []byte
compressed []byte
}{
{
name: "nil",
uncompressed: nil,
compressed: hexToBytes("06"),
},
{
name: "pay-to-pubkey-hash 1",
uncompressed: hexToBytes("76a9141018853670f9f3b0582c5b9ee8ce93764ac32b9388ac"),
compressed: hexToBytes("001018853670f9f3b0582c5b9ee8ce93764ac32b93"),
},
{
name: "pay-to-pubkey-hash 2",
uncompressed: hexToBytes("76a914e34cce70c86373273efcc54ce7d2a491bb4a0e8488ac"),
compressed: hexToBytes("00e34cce70c86373273efcc54ce7d2a491bb4a0e84"),
},
{
name: "pay-to-script-hash 1",
uncompressed: hexToBytes("a914da1745e9b549bd0bfa1a569971c77eba30cd5a4b87"),
compressed: hexToBytes("01da1745e9b549bd0bfa1a569971c77eba30cd5a4b"),
},
{
name: "pay-to-script-hash 2",
uncompressed: hexToBytes("a914f815b036d9bbbce5e9f2a00abd1bf3dc91e9551087"),
compressed: hexToBytes("01f815b036d9bbbce5e9f2a00abd1bf3dc91e95510"),
},
{
name: "pay-to-pubkey compressed 0x02",
uncompressed: hexToBytes("2102192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4ac"),
compressed: hexToBytes("02192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4"),
},
{
name: "pay-to-pubkey compressed 0x03",
uncompressed: hexToBytes("2103b0bd634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e65ac"),
compressed: hexToBytes("03b0bd634234abbb1ba1e986e884185c61cf43e001f9137f23c2c409273eb16e65"),
},
{
name: "pay-to-pubkey uncompressed 0x04 even",
uncompressed: hexToBytes("4104192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b40d45264838c0bd96852662ce6a847b197376830160c6d2eb5e6a4c44d33f453eac"),
compressed: hexToBytes("04192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4"),
},
{
name: "pay-to-pubkey uncompressed 0x04 odd",
uncompressed: hexToBytes("410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac"),
compressed: hexToBytes("0511db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5c"),
},
{
name: "pay-to-pubkey invalid pubkey",
uncompressed: hexToBytes("3302aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac"),
compressed: hexToBytes("293302aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaac"),
},
{
name: "requires 2 size bytes - data push 200 bytes",
uncompressed: append(hexToBytes("4cc8"), bytes.Repeat([]byte{0x00}, 200)...),
// [0x80, 0x50] = 208 as a variable length quantity
// [0x4c, 0xc8] = OP_PUSHDATA1 200
compressed: append(hexToBytes("80504cc8"), bytes.Repeat([]byte{0x00}, 200)...),
},
}
for _, test := range tests {
// Ensure the function to calculate the serialized size without
// actually serializing the value is calculated properly.
gotSize := compressedScriptSize(test.uncompressed)
if gotSize != len(test.compressed) {
t.Errorf("compressedScriptSize (%s): did not get "+
"expected size - got %d, want %d", test.name,
gotSize, len(test.compressed))
continue
}
// Ensure the script compresses to the expected bytes.
gotCompressed := make([]byte, gotSize)
gotBytesWritten := putCompressedScript(gotCompressed,
test.uncompressed)
if !bytes.Equal(gotCompressed, test.compressed) {
t.Errorf("putCompressedScript (%s): did not get "+
"expected bytes - got %x, want %x", test.name,
gotCompressed, test.compressed)
continue
}
if gotBytesWritten != len(test.compressed) {
t.Errorf("putCompressedScript (%s): did not get "+
"expected number of bytes written - got %d, "+
"want %d", test.name, gotBytesWritten,
len(test.compressed))
continue
}
// Ensure the compressed script size is properly decoded from
// the compressed script.
gotDecodedSize := decodeCompressedScriptSize(test.compressed)
if gotDecodedSize != len(test.compressed) {
t.Errorf("decodeCompressedScriptSize (%s): did not get "+
"expected size - got %d, want %d", test.name,
gotDecodedSize, len(test.compressed))
continue
}
// Ensure the script decompresses to the expected bytes.
gotDecompressed := decompressScript(test.compressed)
if !bytes.Equal(gotDecompressed, test.uncompressed) {
t.Errorf("decompressScript (%s): did not get expected "+
"bytes - got %x, want %x", test.name,
gotDecompressed, test.uncompressed)
continue
}
}
}
// TestScriptCompressionErrors ensures calling various functions related to
// script compression with incorrect data returns the expected results.
func TestScriptCompressionErrors(t *testing.T) {
t.Parallel()
// A nil script must result in a decoded size of 0.
if gotSize := decodeCompressedScriptSize(nil); gotSize != 0 {
t.Fatalf("decodeCompressedScriptSize with nil script did not "+
"return 0 - got %d", gotSize)
}
// A nil script must result in a nil decompressed script.
if gotScript := decompressScript(nil); gotScript != nil {
t.Fatalf("decompressScript with nil script did not return nil "+
"decompressed script - got %x", gotScript)
}
// A compressed script for a pay-to-pubkey (uncompressed) that results
// in an invalid pubkey must result in a nil decompressed script.
compressedScript := hexToBytes("04012d74d0cb94344c9569c2e77901573d8d" +
"7903c3ebec3a957724895dca52c6b4")
if gotScript := decompressScript(compressedScript); gotScript != nil {
t.Fatalf("decompressScript with compressed pay-to-"+
"uncompressed-pubkey that is invalid did not return "+
"nil decompressed script - got %x", gotScript)
}
}
// TestAmountCompression ensures the domain-specific transaction output amount
// compression and decompression works as expected.
func TestAmountCompression(t *testing.T) {
t.Parallel()
tests := []struct {
name string
uncompressed uint64
compressed uint64
}{
{
name: "0 KAS",
uncompressed: 0,
compressed: 0,
},
{
name: "546 Sompi (current network dust value)",
uncompressed: 546,
compressed: 4911,
},
{
name: "0.00001 KAS (typical transaction fee)",
uncompressed: 1000,
compressed: 4,
},
{
name: "0.0001 KAS (typical transaction fee)",
uncompressed: 10000,
compressed: 5,
},
{
name: "0.12345678 KAS",
uncompressed: 12345678,
compressed: 111111101,
},
{
name: "0.5 KAS",
uncompressed: 50000000,
compressed: 48,
},
{
name: "1 KAS",
uncompressed: 100000000,
compressed: 9,
},
{
name: "5 KAS",
uncompressed: 500000000,
compressed: 49,
},
{
name: "21000000 KAS (max minted coins)",
uncompressed: 2100000000000000,
compressed: 21000000,
},
}
for _, test := range tests {
// Ensure the amount compresses to the expected value.
gotCompressed := compressTxOutAmount(test.uncompressed)
if gotCompressed != test.compressed {
t.Errorf("compressTxOutAmount (%s): did not get "+
"expected value - got %d, want %d", test.name,
gotCompressed, test.compressed)
continue
}
// Ensure the value decompresses to the expected value.
gotDecompressed := decompressTxOutAmount(test.compressed)
if gotDecompressed != test.uncompressed {
t.Errorf("decompressTxOutAmount (%s): did not get "+
"expected value - got %d, want %d", test.name,
gotDecompressed, test.uncompressed)
continue
}
}
}
// TestCompressedTxOut ensures the transaction output serialization and
// deserialization works as expected.
func TestCompressedTxOut(t *testing.T) {
t.Parallel()
tests := []struct {
name string
amount uint64
scriptPubKey []byte
compressed []byte
}{
{
name: "pay-to-pubkey-hash dust",
amount: 546,
scriptPubKey: hexToBytes("76a9141018853670f9f3b0582c5b9ee8ce93764ac32b9388ac"),
compressed: hexToBytes("a52f001018853670f9f3b0582c5b9ee8ce93764ac32b93"),
},
{
name: "pay-to-pubkey uncompressed 1 KAS",
amount: 100000000,
scriptPubKey: hexToBytes("4104192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b40d45264838c0bd96852662ce6a847b197376830160c6d2eb5e6a4c44d33f453eac"),
compressed: hexToBytes("0904192d74d0cb94344c9569c2e77901573d8d7903c3ebec3a957724895dca52c6b4"),
},
}
for _, test := range tests {
// Ensure the function to calculate the serialized size without
// actually serializing the txout is calculated properly.
gotSize := compressedTxOutSize(test.amount, test.scriptPubKey)
if gotSize != len(test.compressed) {
t.Errorf("compressedTxOutSize (%s): did not get "+
"expected size - got %d, want %d", test.name,
gotSize, len(test.compressed))
continue
}
// Ensure the txout compresses to the expected value.
gotCompressed := make([]byte, gotSize)
gotBytesWritten := putCompressedTxOut(gotCompressed,
test.amount, test.scriptPubKey)
if !bytes.Equal(gotCompressed, test.compressed) {
t.Errorf("compressTxOut (%s): did not get expected "+
"bytes - got %x, want %x", test.name,
gotCompressed, test.compressed)
continue
}
if gotBytesWritten != len(test.compressed) {
t.Errorf("compressTxOut (%s): did not get expected "+
"number of bytes written - got %d, want %d",
test.name, gotBytesWritten,
len(test.compressed))
continue
}
// Ensure the serialized bytes are decoded back to the expected
// uncompressed values.
gotAmount, gotScript, gotBytesRead, err := decodeCompressedTxOut(
test.compressed)
if err != nil {
t.Errorf("decodeCompressedTxOut (%s): unexpected "+
"error: %v", test.name, err)
continue
}
if gotAmount != test.amount {
t.Errorf("decodeCompressedTxOut (%s): did not get "+
"expected amount - got %d, want %d",
test.name, gotAmount, test.amount)
continue
}
if !bytes.Equal(gotScript, test.scriptPubKey) {
t.Errorf("decodeCompressedTxOut (%s): did not get "+
"expected script - got %x, want %x",
test.name, gotScript, test.scriptPubKey)
continue
}
if gotBytesRead != len(test.compressed) {
t.Errorf("decodeCompressedTxOut (%s): did not get "+
"expected number of bytes read - got %d, want %d",
test.name, gotBytesRead, len(test.compressed))
continue
}
}
}
// TestTxOutCompressionErrors ensures calling various functions related to
// txout compression with incorrect data returns the expected results.
func TestTxOutCompressionErrors(t *testing.T) {
t.Parallel()
// A compressed txout with missing compressed script must error.
compressedTxOut := hexToBytes("00")
_, _, _, err := decodeCompressedTxOut(compressedTxOut)
if !isDeserializeErr(err) {
t.Fatalf("decodeCompressedTxOut with missing compressed script "+
"did not return expected error type - got %T, want "+
"errDeserialize", err)
}
// A compressed txout with short compressed script must error.
compressedTxOut = hexToBytes("0010")
_, _, _, err = decodeCompressedTxOut(compressedTxOut)
if !isDeserializeErr(err) {
t.Fatalf("decodeCompressedTxOut with short compressed script "+
"did not return expected error type - got %T, want "+
"errDeserialize", err)
}
}

View File

@ -204,7 +204,7 @@ func TestIsKnownBlock(t *testing.T) {
{hash: dagconfig.SimnetParams.GenesisHash.String(), want: true}, {hash: dagconfig.SimnetParams.GenesisHash.String(), want: true},
// Block 3b should be present (as a second child of Block 2). // Block 3b should be present (as a second child of Block 2).
{hash: "264176fb6072e2362db18f92d3f4b739cff071a206736df7c407c0bf9a1d7fef", want: true}, {hash: "216301e3fc03cf89973b9192b4ecdd732bf3b677cf1ca4f6c340a56f1533fb4f", want: true},
// Block 100000 should be present (as an orphan). // Block 100000 should be present (as an orphan).
{hash: "65b20b048a074793ebfd1196e49341c8d194dabfc6b44a4fd0c607406e122baf", want: true}, {hash: "65b20b048a074793ebfd1196e49341c8d194dabfc6b44a4fd0c607406e122baf", want: true},

View File

@ -10,6 +10,7 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/kaspanet/kaspad/dagconfig" "github.com/kaspanet/kaspad/dagconfig"
"github.com/kaspanet/kaspad/util/buffers"
"github.com/pkg/errors" "github.com/pkg/errors"
"io" "io"
"sync" "sync"
@ -87,22 +88,6 @@ func isNotInDAGErr(err error) bool {
return errors.As(err, &notInDAGErr) return errors.As(err, &notInDAGErr)
} }
// errDeserialize signifies that a problem was encountered when deserializing
// data.
type errDeserialize string
// Error implements the error interface.
func (e errDeserialize) Error() string {
return string(e)
}
// isDeserializeErr returns whether or not the passed error is an errDeserialize
// error.
func isDeserializeErr(err error) bool {
var deserializeErr errDeserialize
return errors.As(err, &deserializeErr)
}
// dbPutVersion uses an existing database transaction to update the provided // dbPutVersion uses an existing database transaction to update the provided
// key in the metadata bucket to the given version. It is primarily used to // key in the metadata bucket to the given version. It is primarily used to
// track versions on entities such as buckets. // track versions on entities such as buckets.
@ -112,115 +97,46 @@ func dbPutVersion(dbTx database.Tx, key []byte, version uint32) error {
return dbTx.Metadata().Put(key, serialized[:]) return dbTx.Metadata().Put(key, serialized[:])
} }
// ----------------------------------------------------------------------------- // outpointKeyPool defines a concurrent safe free list of byte buffers used to
// The unspent transaction output (UTXO) set consists of an entry for each
// unspent output using a format that is optimized to reduce space using domain
// specific compression algorithms.
//
// Each entry is keyed by an outpoint as specified below. It is important to
// note that the key encoding uses a VLQ, which employs an MSB encoding so
// iteration of UTXOs when doing byte-wise comparisons will produce them in
// order.
//
// The serialized key format is:
// <hash><output index>
//
// Field Type Size
// hash daghash.Hash daghash.HashSize
// output index VLQ variable
//
// The serialized value format is:
//
// <header code><compressed txout>
//
// Field Type Size
// header code VLQ variable
// compressed txout
// compressed amount VLQ variable
// compressed script []byte variable
//
// The serialized header code format is:
// bit 0 - containing transaction is a coinbase
// bits 1-x - height of the block that contains the unspent txout
//
// Example 1:
// b7c3332bc138e2c9429818f5fed500bcc1746544218772389054dc8047d7cd3f:0
//
// 03320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52
// <><------------------------------------------------------------------>
// | |
// header code compressed txout
//
// - header code: 0x03 (coinbase, height 1)
// - compressed txout:
// - 0x32: VLQ-encoded compressed amount for 5000000000 (50 KAS)
// - 0x04: special script type pay-to-pubkey
// - 0x96...52: x-coordinate of the pubkey
//
// Example 2:
// 4a16969aa4764dd7507fc1de7f0baa4850a246de90c45e59a3207f9a26b5036f:2
//
// 8cf316800900b8025be1b3efc63b0ad48e7f9f10e87544528d58
// <----><------------------------------------------>
// | |
// header code compressed txout
//
// - header code: 0x8cf316 (not coinbase, height 113931)
// - compressed txout:
// - 0x8009: VLQ-encoded compressed amount for 15000000 (0.15 KAS)
// - 0x00: special script type pay-to-pubkey-hash
// - 0xb8...58: pubkey hash
//
// Example 3:
// 1b02d1c8cfef60a189017b9a420c682cf4a0028175f2f563209e4ff61c8c3620:22
//
// a8a2588ba5b9e763011dd46a006572d820e448e12d2bbb38640bc718e6
// <----><-------------------------------------------------->
// | |
// header code compressed txout
//
// - header code: 0xa8a258 (not coinbase, height 338156)
// - compressed txout:
// - 0x8ba5b9e763: VLQ-encoded compressed amount for 366875659 (3.66875659 KAS)
// - 0x01: special script type pay-to-script-hash
// - 0x1d...e6: script hash
// -----------------------------------------------------------------------------
// maxUint32VLQSerializeSize is the maximum number of bytes a max uint32 takes
// to serialize as a VLQ.
var maxUint32VLQSerializeSize = serializeSizeVLQ(1<<32 - 1)
// outpointKeyPool defines a concurrent safe free list of byte slices used to
// provide temporary buffers for outpoint database keys. // provide temporary buffers for outpoint database keys.
var outpointKeyPool = sync.Pool{ var outpointKeyPool = sync.Pool{
New: func() interface{} { New: func() interface{} {
b := make([]byte, daghash.HashSize+maxUint32VLQSerializeSize) return &bytes.Buffer{} // Pointer to a buffer to avoid boxing alloc.
return &b // Pointer to slice to avoid boxing alloc.
}, },
} }
// outpointKey returns a key suitable for use as a database key in the UTXO set // outpointIndexByteOrder is the byte order for serializing the outpoint index.
// while making use of a free list. A new buffer is allocated if there are not // It uses big endian to ensure that when outpoint is used as database key, the
// already any available on the free list. The returned byte slice should be // keys will be iterated in an ascending order by the outpoint index.
// returned to the free list by using the recycleOutpointKey function when the var outpointIndexByteOrder = binary.BigEndian
// caller is done with it _unless_ the slice will need to live for longer than
// the caller can calculate such as when used to write to the database. func serializeOutpoint(w io.Writer, outpoint *wire.Outpoint) error {
func outpointKey(outpoint wire.Outpoint) *[]byte { _, err := w.Write(outpoint.TxID[:])
// A VLQ employs an MSB encoding, so they are useful not only to reduce if err != nil {
// the amount of storage space, but also so iteration of UTXOs when return err
// doing byte-wise comparisons will produce them in order.
key := outpointKeyPool.Get().(*[]byte)
idx := uint64(outpoint.Index)
*key = (*key)[:daghash.HashSize+serializeSizeVLQ(idx)]
copy(*key, outpoint.TxID[:])
putVLQ((*key)[daghash.HashSize:], idx)
return key
} }
// recycleOutpointKey puts the provided byte slice, which should have been return binaryserializer.PutUint32(w, outpointIndexByteOrder, outpoint.Index)
// obtained via the outpointKey function, back on the free list. }
func recycleOutpointKey(key *[]byte) {
outpointKeyPool.Put(key) var outpointSerializeSize = daghash.TxIDSize + 4
// deserializeOutpoint decodes an outpoint from the passed serialized byte
// slice into a new wire.Outpoint using a format that is suitable for long-
// term storage. This format is described in detail above.
func deserializeOutpoint(r io.Reader) (*wire.Outpoint, error) {
outpoint := &wire.Outpoint{}
_, err := r.Read(outpoint.TxID[:])
if err != nil {
return nil, err
}
outpoint.Index, err = binaryserializer.Uint32(r, outpointIndexByteOrder)
if err != nil {
return nil, err
}
return outpoint, nil
} }
// dbPutUTXODiff uses an existing database transaction to update the UTXO set // dbPutUTXODiff uses an existing database transaction to update the UTXO set
@ -230,20 +146,42 @@ func recycleOutpointKey(key *[]byte) {
func dbPutUTXODiff(dbTx database.Tx, diff *UTXODiff) error { func dbPutUTXODiff(dbTx database.Tx, diff *UTXODiff) error {
utxoBucket := dbTx.Metadata().Bucket(utxoSetBucketName) utxoBucket := dbTx.Metadata().Bucket(utxoSetBucketName)
for outpoint := range diff.toRemove { for outpoint := range diff.toRemove {
key := outpointKey(outpoint) w := outpointKeyPool.Get().(*bytes.Buffer)
err := utxoBucket.Delete(*key) w.Reset()
recycleOutpointKey(key) err := serializeOutpoint(w, &outpoint)
if err != nil { if err != nil {
return err return err
} }
key := w.Bytes()
err = utxoBucket.Delete(key)
if err != nil {
return err
}
outpointKeyPool.Put(w)
} }
// We are preallocating for P2PKH entries because they are the most common ones.
// If we have entries with a compressed script bigger than P2PKH's, the buffer will grow.
bytesToPreallocate := (p2pkhUTXOEntrySerializeSize + outpointSerializeSize) * len(diff.toAdd)
buff := bytes.NewBuffer(make([]byte, bytesToPreallocate))
for outpoint, entry := range diff.toAdd { for outpoint, entry := range diff.toAdd {
// Serialize and store the UTXO entry. // Serialize and store the UTXO entry.
serialized := serializeUTXOEntry(entry) sBuff := buffers.NewSubBuffer(buff)
err := serializeUTXOEntry(sBuff, entry)
if err != nil {
return err
}
serializedEntry := sBuff.Bytes()
key := outpointKey(outpoint) sBuff = buffers.NewSubBuffer(buff)
err := utxoBucket.Put(*key, serialized) err = serializeOutpoint(sBuff, &outpoint)
if err != nil {
return err
}
key := sBuff.Bytes()
err = utxoBucket.Put(key, serializedEntry)
// NOTE: The key is intentionally not recycled here since the // NOTE: The key is intentionally not recycled here since the
// database interface contract prohibits modifications. It will // database interface contract prohibits modifications. It will
// be garbage collected normally when the database is done with // be garbage collected normally when the database is done with
@ -532,32 +470,14 @@ func (dag *BlockDAG) initDAGState() error {
fullUTXOCollection := make(utxoCollection, utxoEntryCount) fullUTXOCollection := make(utxoCollection, utxoEntryCount)
for ok := cursor.First(); ok; ok = cursor.Next() { for ok := cursor.First(); ok; ok = cursor.Next() {
// Deserialize the outpoint // Deserialize the outpoint
outpoint, err := deserializeOutpoint(cursor.Key()) outpoint, err := deserializeOutpoint(bytes.NewReader(cursor.Key()))
if err != nil { if err != nil {
// Ensure any deserialization errors are returned as database
// corruption errors.
if isDeserializeErr(err) {
return database.Error{
ErrorCode: database.ErrCorruption,
Description: fmt.Sprintf("corrupt outpoint: %s", err),
}
}
return err return err
} }
// Deserialize the utxo entry // Deserialize the utxo entry
entry, err := deserializeUTXOEntry(cursor.Value()) entry, err := deserializeUTXOEntry(bytes.NewReader(cursor.Value()))
if err != nil { if err != nil {
// Ensure any deserialization errors are returned as database
// corruption errors.
if isDeserializeErr(err) {
return database.Error{
ErrorCode: database.ErrCorruption,
Description: fmt.Sprintf("corrupt utxo entry: %s", err),
}
}
return err return err
} }

View File

@ -6,6 +6,7 @@ package blockdag
import ( import (
"bytes" "bytes"
"encoding/hex"
"github.com/pkg/errors" "github.com/pkg/errors"
"reflect" "reflect"
"testing" "testing"
@ -36,9 +37,21 @@ func TestErrNotInDAG(t *testing.T) {
} }
} }
// TestUtxoSerialization ensures serializing and deserializing unspent // hexToBytes converts the passed hex string into bytes and will panic if there
// is an error. This is only provided for the hard-coded constants so errors in
// the source code can be detected. It will only (and must only) be called with
// hard-coded values.
func hexToBytes(s string) []byte {
b, err := hex.DecodeString(s)
if err != nil {
panic("invalid hex in source file: " + s)
}
return b
}
// TestUTXOSerialization ensures serializing and deserializing unspent
// trasaction output entries works as expected. // trasaction output entries works as expected.
func TestUtxoSerialization(t *testing.T) { func TestUTXOSerialization(t *testing.T) {
t.Parallel() t.Parallel()
tests := []struct { tests := []struct {
@ -54,7 +67,7 @@ func TestUtxoSerialization(t *testing.T) {
blockBlueScore: 1, blockBlueScore: 1,
packedFlags: tfCoinbase, packedFlags: tfCoinbase,
}, },
serialized: hexToBytes("03320496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52"), serialized: hexToBytes("030000000000000000f2052a0100000043410496b538e853519c726a2c91e61ec11600ae1390813a627c66fb8be7947be63c52da7589379515d4e0a604f8141781e62294721166bf621e73a82cbf2342c858eeac"),
}, },
{ {
name: "blue score 100001, not coinbase", name: "blue score 100001, not coinbase",
@ -64,13 +77,21 @@ func TestUtxoSerialization(t *testing.T) {
blockBlueScore: 100001, blockBlueScore: 100001,
packedFlags: 0, packedFlags: 0,
}, },
serialized: hexToBytes("8b99420700ee8bd501094a7d5ca318da2506de35e1cb025ddc"), serialized: hexToBytes("420d03000000000040420f00000000001976a914ee8bd501094a7d5ca318da2506de35e1cb025ddc88ac"),
}, },
} }
for i, test := range tests { for i, test := range tests {
// Ensure the utxo entry serializes to the expected value. // Ensure the utxo entry serializes to the expected value.
gotBytes := serializeUTXOEntry(test.entry) w := &bytes.Buffer{}
err := serializeUTXOEntry(w, test.entry)
if err != nil {
t.Errorf("serializeUTXOEntry #%d (%s) unexpected "+
"error: %v", i, test.name, err)
continue
}
gotBytes := w.Bytes()
if !bytes.Equal(gotBytes, test.serialized) { if !bytes.Equal(gotBytes, test.serialized) {
t.Errorf("serializeUTXOEntry #%d (%s): mismatched "+ t.Errorf("serializeUTXOEntry #%d (%s): mismatched "+
"bytes - got %x, want %x", i, test.name, "bytes - got %x, want %x", i, test.name,
@ -78,8 +99,8 @@ func TestUtxoSerialization(t *testing.T) {
continue continue
} }
// Deserialize to a utxo entry. // Deserialize to a utxo entry.gotBytes
utxoEntry, err := deserializeUTXOEntry(test.serialized) utxoEntry, err := deserializeUTXOEntry(bytes.NewReader(test.serialized))
if err != nil { if err != nil {
t.Errorf("deserializeUTXOEntry #%d (%s) unexpected "+ t.Errorf("deserializeUTXOEntry #%d (%s) unexpected "+
"error: %v", i, test.name, err) "error: %v", i, test.name, err)
@ -124,28 +145,24 @@ func TestUtxoEntryDeserializeErrors(t *testing.T) {
tests := []struct { tests := []struct {
name string name string
serialized []byte serialized []byte
errType error
}{ }{
{ {
name: "no data after header code", name: "no data after header code",
serialized: hexToBytes("02"), serialized: hexToBytes("02"),
errType: errDeserialize(""),
}, },
{ {
name: "incomplete compressed txout", name: "incomplete compressed txout",
serialized: hexToBytes("0232"), serialized: hexToBytes("0232"),
errType: errDeserialize(""),
}, },
} }
for _, test := range tests { for _, test := range tests {
// Ensure the expected error type is returned and the returned // Ensure the expected error type is returned and the returned
// entry is nil. // entry is nil.
entry, err := deserializeUTXOEntry(test.serialized) entry, err := deserializeUTXOEntry(bytes.NewReader(test.serialized))
if reflect.TypeOf(err) != reflect.TypeOf(test.errType) { if err == nil {
t.Errorf("deserializeUTXOEntry (%s): expected error "+ t.Errorf("deserializeUTXOEntry (%s): didn't return an error",
"type does not match - got %T, want %T", test.name)
test.name, err, test.errType)
continue continue
} }
if entry != nil { if entry != nil {

View File

@ -33,7 +33,7 @@ func TestGHOSTDAG(t *testing.T) {
}{ }{
{ {
k: 3, k: 3,
expectedReds: []string{"F", "G", "H", "I", "O", "P"}, expectedReds: []string{"F", "G", "H", "I", "N", "Q"},
dagData: []*testBlockData{ dagData: []*testBlockData{
{ {
parents: []string{"A"}, parents: []string{"A"},
@ -166,7 +166,7 @@ func TestGHOSTDAG(t *testing.T) {
id: "T", id: "T",
expectedScore: 13, expectedScore: 13,
expectedSelectedParent: "S", expectedSelectedParent: "S",
expectedBlues: []string{"S", "N", "Q"}, expectedBlues: []string{"S", "O", "P"},
}, },
}, },
}, },

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -3,7 +3,7 @@ package blockdag
import ( import (
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"fmt" "github.com/kaspanet/kaspad/util/binaryserializer"
"github.com/pkg/errors" "github.com/pkg/errors"
"io" "io"
"math/big" "math/big"
@ -54,40 +54,26 @@ func utxoEntryHeaderCode(entry *UTXOEntry) uint64 {
return headerCode return headerCode
} }
func (diffStore *utxoDiffStore) deserializeBlockUTXODiffData(serializedDiffDataBytes []byte) (*blockUTXODiffData, error) { func (diffStore *utxoDiffStore) deserializeBlockUTXODiffData(serializedDiffData []byte) (*blockUTXODiffData, error) {
diffData := &blockUTXODiffData{} diffData := &blockUTXODiffData{}
serializedDiffData := bytes.NewBuffer(serializedDiffDataBytes) r := bytes.NewBuffer(serializedDiffData)
var hasDiffChild bool var hasDiffChild bool
err := wire.ReadElement(serializedDiffData, &hasDiffChild) err := wire.ReadElement(r, &hasDiffChild)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if hasDiffChild { if hasDiffChild {
hash := &daghash.Hash{} hash := &daghash.Hash{}
err := wire.ReadElement(serializedDiffData, hash) err := wire.ReadElement(r, hash)
if err != nil { if err != nil {
return nil, err return nil, err
} }
diffData.diffChild = diffStore.dag.index.LookupNode(hash) diffData.diffChild = diffStore.dag.index.LookupNode(hash)
} }
diffData.diff = &UTXODiff{ diffData.diff, err = deserializeUTXODiff(r)
useMultiset: true,
}
diffData.diff.toAdd, err = deserializeDiffEntries(serializedDiffData)
if err != nil {
return nil, err
}
diffData.diff.toRemove, err = deserializeDiffEntries(serializedDiffData)
if err != nil {
return nil, err
}
diffData.diff.diffMultiset, err = deserializeMultiset(serializedDiffData)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -95,38 +81,38 @@ func (diffStore *utxoDiffStore) deserializeBlockUTXODiffData(serializedDiffDataB
return diffData, nil return diffData, nil
} }
func deserializeDiffEntries(r io.Reader) (utxoCollection, error) { func deserializeUTXODiff(r io.Reader) (*UTXODiff, error) {
diff := &UTXODiff{
useMultiset: true,
}
var err error
diff.toAdd, err = deserializeUTXOCollection(r)
if err != nil {
return nil, err
}
diff.toRemove, err = deserializeUTXOCollection(r)
if err != nil {
return nil, err
}
diff.diffMultiset, err = deserializeMultiset(r)
if err != nil {
return nil, err
}
return diff, nil
}
func deserializeUTXOCollection(r io.Reader) (utxoCollection, error) {
count, err := wire.ReadVarInt(r) count, err := wire.ReadVarInt(r)
if err != nil { if err != nil {
return nil, err return nil, err
} }
collection := utxoCollection{} collection := utxoCollection{}
for i := uint64(0); i < count; i++ { for i := uint64(0); i < count; i++ {
outpointSize, err := wire.ReadVarInt(r) utxoEntry, outpoint, err := deserializeUTXO(r)
if err != nil {
return nil, err
}
serializedOutpoint := make([]byte, outpointSize)
err = binary.Read(r, byteOrder, serializedOutpoint)
if err != nil {
return nil, err
}
outpoint, err := deserializeOutpoint(serializedOutpoint)
if err != nil {
return nil, err
}
utxoEntrySize, err := wire.ReadVarInt(r)
if err != nil {
return nil, err
}
serializedEntry := make([]byte, utxoEntrySize)
err = binary.Read(r, byteOrder, serializedEntry)
if err != nil {
return nil, err
}
utxoEntry, err := deserializeUTXOEntry(serializedEntry)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -135,6 +121,19 @@ func deserializeDiffEntries(r io.Reader) (utxoCollection, error) {
return collection, nil return collection, nil
} }
func deserializeUTXO(r io.Reader) (*UTXOEntry, *wire.Outpoint, error) {
outpoint, err := deserializeOutpoint(r)
if err != nil {
return nil, nil, err
}
utxoEntry, err := deserializeUTXOEntry(r)
if err != nil {
return nil, nil, err
}
return utxoEntry, outpoint, nil
}
// deserializeMultiset deserializes an EMCH multiset. // deserializeMultiset deserializes an EMCH multiset.
// See serializeMultiset for more details. // See serializeMultiset for more details.
func deserializeMultiset(r io.Reader) (*ecc.Multiset, error) { func deserializeMultiset(r io.Reader) (*ecc.Multiset, error) {
@ -218,96 +217,93 @@ func serializeMultiset(w io.Writer, ms *ecc.Multiset) error {
// serializeUTXO serializes a utxo entry-outpoint pair // serializeUTXO serializes a utxo entry-outpoint pair
func serializeUTXO(w io.Writer, entry *UTXOEntry, outpoint *wire.Outpoint) error { func serializeUTXO(w io.Writer, entry *UTXOEntry, outpoint *wire.Outpoint) error {
serializedOutpoint := *outpointKey(*outpoint) err := serializeOutpoint(w, outpoint)
err := wire.WriteVarInt(w, uint64(len(serializedOutpoint)))
if err != nil { if err != nil {
return err return err
} }
err = binary.Write(w, byteOrder, serializedOutpoint) err = serializeUTXOEntry(w, entry)
if err != nil {
return err
}
serializedUTXOEntry := serializeUTXOEntry(entry)
err = wire.WriteVarInt(w, uint64(len(serializedUTXOEntry)))
if err != nil {
return err
}
err = binary.Write(w, byteOrder, serializedUTXOEntry)
if err != nil { if err != nil {
return err return err
} }
return nil return nil
} }
// serializeUTXOEntry returns the entry serialized to a format that is suitable // p2pkhUTXOEntrySerializeSize is the serialized size for a P2PKH UTXO entry.
// for long-term storage. The format is described in detail above. // 8 bytes (header code) + 8 bytes (amount) + varint for script pub key length of 25 (for P2PKH) + 25 bytes for P2PKH script.
func serializeUTXOEntry(entry *UTXOEntry) []byte { var p2pkhUTXOEntrySerializeSize = 8 + 8 + wire.VarIntSerializeSize(25) + 25
// serializeUTXOEntry encodes the entry to the given io.Writer and use compression if useCompression is true.
// The compression format is described in detail above.
func serializeUTXOEntry(w io.Writer, entry *UTXOEntry) error {
// Encode the header code. // Encode the header code.
headerCode := utxoEntryHeaderCode(entry) headerCode := utxoEntryHeaderCode(entry)
// Calculate the size needed to serialize the entry. err := binaryserializer.PutUint64(w, byteOrder, headerCode)
size := serializeSizeVLQ(headerCode) + if err != nil {
compressedTxOutSize(uint64(entry.Amount()), entry.ScriptPubKey()) return err
// Serialize the header code followed by the compressed unspent
// transaction output.
serialized := make([]byte, size)
offset := putVLQ(serialized, headerCode)
offset += putCompressedTxOut(serialized[offset:], uint64(entry.Amount()),
entry.ScriptPubKey())
return serialized
} }
// deserializeOutpoint decodes an outpoint from the passed serialized byte err = binaryserializer.PutUint64(w, byteOrder, entry.Amount())
// slice into a new wire.Outpoint using a format that is suitable for long- if err != nil {
// term storage. this format is described in detail above. return err
func deserializeOutpoint(serialized []byte) (*wire.Outpoint, error) {
if len(serialized) <= daghash.HashSize {
return nil, errDeserialize("unexpected end of data")
} }
txID := daghash.TxID{} err = wire.WriteVarInt(w, uint64(len(entry.ScriptPubKey())))
txID.SetBytes(serialized[:daghash.HashSize]) if err != nil {
index, _ := deserializeVLQ(serialized[daghash.HashSize:]) return err
return wire.NewOutpoint(&txID, uint32(index)), nil
} }
// deserializeUTXOEntry decodes a UTXO entry from the passed serialized byte _, err = w.Write(entry.ScriptPubKey())
// slice into a new UTXOEntry using a format that is suitable for long-term if err != nil {
// storage. The format is described in detail above. return errors.WithStack(err)
func deserializeUTXOEntry(serialized []byte) (*UTXOEntry, error) { }
return nil
}
// deserializeUTXOEntry decodes a UTXO entry from the passed reader
// into a new UTXOEntry. If isCompressed is used it will decompress
// the entry according to the format that is described in detail
// above.
func deserializeUTXOEntry(r io.Reader) (*UTXOEntry, error) {
// Deserialize the header code. // Deserialize the header code.
code, offset := deserializeVLQ(serialized) headerCode, err := binaryserializer.Uint64(r, byteOrder)
if offset >= len(serialized) { if err != nil {
return nil, errDeserialize("unexpected end of data after header") return nil, err
} }
// Decode the header code. // Decode the header code.
// //
// Bit 0 indicates whether the containing transaction is a coinbase. // Bit 0 indicates whether the containing transaction is a coinbase.
// Bits 1-x encode blue score of the containing transaction. // Bits 1-x encode blue score of the containing transaction.
isCoinbase := code&0x01 != 0 isCoinbase := headerCode&0x01 != 0
blockBlueScore := code >> 1 blockBlueScore := headerCode >> 1
// Decode the compressed unspent transaction output.
amount, scriptPubKey, _, err := decodeCompressedTxOut(serialized[offset:])
if err != nil {
return nil, errDeserialize(fmt.Sprintf("unable to decode "+
"UTXO: %s", err))
}
entry := &UTXOEntry{ entry := &UTXOEntry{
amount: amount,
scriptPubKey: scriptPubKey,
blockBlueScore: blockBlueScore, blockBlueScore: blockBlueScore,
packedFlags: 0, packedFlags: 0,
} }
if isCoinbase { if isCoinbase {
entry.packedFlags |= tfCoinbase entry.packedFlags |= tfCoinbase
} }
entry.amount, err = binaryserializer.Uint64(r, byteOrder)
if err != nil {
return nil, err
}
scriptPubKeyLen, err := wire.ReadVarInt(r)
if err != nil {
return nil, err
}
entry.scriptPubKey = make([]byte, scriptPubKeyLen)
_, err = r.Read(entry.scriptPubKey)
if err != nil {
return nil, errors.WithStack(err)
}
return entry, nil return entry, nil
} }

View File

@ -116,7 +116,7 @@ func TestUTXODiff(t *testing.T) {
// Test utxoDiff string representation // Test utxoDiff string representation
expectedDiffString := "toAdd: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ]; toRemove: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ]" expectedDiffString := "toAdd: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ]; toRemove: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ]"
if withMultiset { if withMultiset {
expectedDiffString = "toAdd: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ]; toRemove: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ], Multiset-Hash: 7cb61e48005b0c817211d04589d719bff87d86a6a6ce2454515f57265382ded7" expectedDiffString = "toAdd: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ]; toRemove: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ], Multiset-Hash: 75cb5bbca4e52a9e478dd3c5af3c856ed0f61b848088014c8c52e70432233a57"
} }
diffString := clonedDiff.String() diffString := clonedDiff.String()
if diffString != expectedDiffString { if diffString != expectedDiffString {
@ -847,7 +847,7 @@ func TestDiffUTXOSet(t *testing.T) {
toRemove: utxoCollection{}, toRemove: utxoCollection{},
}, },
}, },
expectedString: "{Base: [ ], To Add: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], To Remove: [ ], Multiset-Hash:da4768bd0359c3426268d6707c1fc17a68c45ef1ea734331b07568418234487f}", expectedString: "{Base: [ ], To Add: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], To Remove: [ ], Multiset-Hash:2103b44fd413f5c28b5ad9afe7c3bebf19afae01cc202ea63b2f29d26252948d}",
expectedCollection: utxoCollection{outpoint0: utxoEntry0}, expectedCollection: utxoCollection{outpoint0: utxoEntry0},
}, },
{ {
@ -860,7 +860,7 @@ func TestDiffUTXOSet(t *testing.T) {
}, },
}, },
expectedMeldSet: nil, expectedMeldSet: nil,
expectedString: "{Base: [ ], To Add: [ ], To Remove: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], Multiset-Hash:046242cb1bb1e6d3fd91d0f181e1b2d4a597ac57fa2584fc3c2eb0e0f46c9369}", expectedString: "{Base: [ ], To Add: [ ], To Remove: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], Multiset-Hash:bae77d5370880c238265d07b80bdadbc6085990b2d2543a5f8beb2e3bd99b25b}",
expectedCollection: utxoCollection{}, expectedCollection: utxoCollection{},
expectedMeldToBaseError: "Couldn't remove outpoint 0000000000000000000000000000000000000000000000000000000000000000:0 because it doesn't exist in the DiffUTXOSet base", expectedMeldToBaseError: "Couldn't remove outpoint 0000000000000000000000000000000000000000000000000000000000000000:0 because it doesn't exist in the DiffUTXOSet base",
}, },
@ -885,7 +885,7 @@ func TestDiffUTXOSet(t *testing.T) {
toRemove: utxoCollection{}, toRemove: utxoCollection{},
}, },
}, },
expectedString: "{Base: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], To Add: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ], To Remove: [ ], Multiset-Hash:556cc61fd4d7e74d7807ca2298c5320375a6a20310a18920e54667220924baff}", expectedString: "{Base: [ (0000000000000000000000000000000000000000000000000000000000000000, 0) => 10, blueScore: 0 ], To Add: [ (1111111111111111111111111111111111111111111111111111111111111111, 0) => 20, blueScore: 1 ], To Remove: [ ], Multiset-Hash:c9932064f2f4ba1940d63b747e4a18053d0b022d66894a2ec1bbf88f74570e93}",
expectedCollection: utxoCollection{ expectedCollection: utxoCollection{
outpoint0: utxoEntry0, outpoint0: utxoEntry0,
outpoint1: utxoEntry1, outpoint1: utxoEntry1,

View File

@ -2,6 +2,7 @@ package binaryserializer
import ( import (
"encoding/binary" "encoding/binary"
"github.com/pkg/errors"
"io" "io"
) )
@ -37,7 +38,7 @@ func Uint8(r io.Reader) (uint8, error) {
buf := Borrow()[:1] buf := Borrow()[:1]
if _, err := io.ReadFull(r, buf); err != nil { if _, err := io.ReadFull(r, buf); err != nil {
Return(buf) Return(buf)
return 0, err return 0, errors.WithStack(err)
} }
rv := buf[0] rv := buf[0]
Return(buf) Return(buf)
@ -51,7 +52,7 @@ func Uint16(r io.Reader, byteOrder binary.ByteOrder) (uint16, error) {
buf := Borrow()[:2] buf := Borrow()[:2]
if _, err := io.ReadFull(r, buf); err != nil { if _, err := io.ReadFull(r, buf); err != nil {
Return(buf) Return(buf)
return 0, err return 0, errors.WithStack(err)
} }
rv := byteOrder.Uint16(buf) rv := byteOrder.Uint16(buf)
Return(buf) Return(buf)
@ -65,7 +66,7 @@ func Uint32(r io.Reader, byteOrder binary.ByteOrder) (uint32, error) {
buf := Borrow()[:4] buf := Borrow()[:4]
if _, err := io.ReadFull(r, buf); err != nil { if _, err := io.ReadFull(r, buf); err != nil {
Return(buf) Return(buf)
return 0, err return 0, errors.WithStack(err)
} }
rv := byteOrder.Uint32(buf) rv := byteOrder.Uint32(buf)
Return(buf) Return(buf)
@ -79,7 +80,7 @@ func Uint64(r io.Reader, byteOrder binary.ByteOrder) (uint64, error) {
buf := Borrow()[:8] buf := Borrow()[:8]
if _, err := io.ReadFull(r, buf); err != nil { if _, err := io.ReadFull(r, buf); err != nil {
Return(buf) Return(buf)
return 0, err return 0, errors.WithStack(err)
} }
rv := byteOrder.Uint64(buf) rv := byteOrder.Uint64(buf)
Return(buf) Return(buf)
@ -93,7 +94,7 @@ func PutUint8(w io.Writer, val uint8) error {
buf[0] = val buf[0] = val
_, err := w.Write(buf) _, err := w.Write(buf)
Return(buf) Return(buf)
return err return errors.WithStack(err)
} }
// PutUint16 serializes the provided uint16 using the given byte order into a // PutUint16 serializes the provided uint16 using the given byte order into a
@ -104,7 +105,7 @@ func PutUint16(w io.Writer, byteOrder binary.ByteOrder, val uint16) error {
byteOrder.PutUint16(buf, val) byteOrder.PutUint16(buf, val)
_, err := w.Write(buf) _, err := w.Write(buf)
Return(buf) Return(buf)
return err return errors.WithStack(err)
} }
// PutUint32 serializes the provided uint32 using the given byte order into a // PutUint32 serializes the provided uint32 using the given byte order into a
@ -115,7 +116,7 @@ func PutUint32(w io.Writer, byteOrder binary.ByteOrder, val uint32) error {
byteOrder.PutUint32(buf, val) byteOrder.PutUint32(buf, val)
_, err := w.Write(buf) _, err := w.Write(buf)
Return(buf) Return(buf)
return err return errors.WithStack(err)
} }
// PutUint64 serializes the provided uint64 using the given byte order into a // PutUint64 serializes the provided uint64 using the given byte order into a
@ -126,7 +127,7 @@ func PutUint64(w io.Writer, byteOrder binary.ByteOrder, val uint64) error {
byteOrder.PutUint64(buf, val) byteOrder.PutUint64(buf, val)
_, err := w.Write(buf) _, err := w.Write(buf)
Return(buf) Return(buf)
return err return errors.WithStack(err)
} }
// binaryFreeList provides a free list of buffers to use for serializing and // binaryFreeList provides a free list of buffers to use for serializing and

View File

@ -254,7 +254,7 @@ func TestBlockErrors(t *testing.T) {
// Truncate the block byte buffer to force errors. // Truncate the block byte buffer to force errors.
shortBytes := block100000Bytes[:186] shortBytes := block100000Bytes[:186]
_, err = util.NewBlockFromBytes(shortBytes) _, err = util.NewBlockFromBytes(shortBytes)
if err != io.EOF { if !errors.Is(err, io.EOF) {
t.Errorf("NewBlockFromBytes: did not get expected error - "+ t.Errorf("NewBlockFromBytes: did not get expected error - "+
"got %v, want %v", err, io.EOF) "got %v, want %v", err, io.EOF)
} }
@ -289,7 +289,7 @@ func TestBlockErrors(t *testing.T) {
// inject a short byte buffer. // inject a short byte buffer.
b.SetBlockBytes(shortBytes) b.SetBlockBytes(shortBytes)
_, err = b.TxLoc() _, err = b.TxLoc()
if err != io.EOF { if !errors.Is(err, io.EOF) {
t.Errorf("TxLoc: did not get expected error - "+ t.Errorf("TxLoc: did not get expected error - "+
"got %v, want %v", err, io.EOF) "got %v, want %v", err, io.EOF)
} }

View File

@ -0,0 +1,48 @@
package buffers
import (
"bytes"
"github.com/pkg/errors"
)
// SubBuffer lets you write to an existing buffer
// and let you check with the `Bytes()` method what
// has been written to the underlying buffer using
// the sub buffer.
type SubBuffer struct {
buff *bytes.Buffer
start, end int
}
// Bytes returns all the bytes that were written to the sub buffer.
func (s *SubBuffer) Bytes() []byte {
return s.buff.Bytes()[s.start:s.end]
}
// Write writes to the sub buffer's underlying buffer
// and increases s.end by the number of bytes written
// so s.Bytes() will be able to return the written bytes.
func (s *SubBuffer) Write(p []byte) (int, error) {
if s.buff.Len() > s.end || s.buff.Len() < s.start {
return 0, errors.New("a sub buffer cannot be written after another entity wrote or read from its " +
"underlying buffer")
}
n, err := s.buff.Write(p)
if err != nil {
return 0, err
}
s.end += n
return n, nil
}
// NewSubBuffer returns a new sub buffer.
func NewSubBuffer(buff *bytes.Buffer) *SubBuffer {
return &SubBuffer{
buff: buff,
start: buff.Len(),
end: buff.Len(),
}
}

View File

@ -2,6 +2,7 @@ package random
import ( import (
"fmt" "fmt"
"github.com/pkg/errors"
"io" "io"
"testing" "testing"
) )
@ -62,7 +63,7 @@ func TestRandomUint64Errors(t *testing.T) {
// Test short reads. // Test short reads.
fr := &fakeRandReader{n: 2, err: io.EOF} fr := &fakeRandReader{n: 2, err: io.EOF}
nonce, err := randomUint64(fr) nonce, err := randomUint64(fr)
if err != io.ErrUnexpectedEOF { if !errors.Is(err, io.ErrUnexpectedEOF) {
t.Errorf("Error not expected value of %v [%v]", t.Errorf("Error not expected value of %v [%v]",
io.ErrUnexpectedEOF, err) io.ErrUnexpectedEOF, err)
} }

View File

@ -6,6 +6,7 @@ package util_test
import ( import (
"bytes" "bytes"
"github.com/pkg/errors"
"io" "io"
"reflect" "reflect"
"testing" "testing"
@ -107,7 +108,7 @@ func TestTxErrors(t *testing.T) {
// Truncate the transaction byte buffer to force errors. // Truncate the transaction byte buffer to force errors.
shortBytes := testTxBytes[:4] shortBytes := testTxBytes[:4]
_, err = util.NewTxFromBytes(shortBytes) _, err = util.NewTxFromBytes(shortBytes)
if err != io.EOF { if !errors.Is(err, io.EOF) {
t.Errorf("NewTxFromBytes: did not get expected error - "+ t.Errorf("NewTxFromBytes: did not get expected error - "+
"got %v, want %v", err, io.EOF) "got %v, want %v", err, io.EOF)
} }

View File

@ -350,7 +350,7 @@ func ReadVarInt(r io.Reader) (uint64, error) {
// encoded using fewer bytes. // encoded using fewer bytes.
min := uint64(0x100000000) min := uint64(0x100000000)
if rv < min { if rv < min {
return 0, messageError("ReadVarInt", fmt.Sprintf( return 0, messageError("readVarInt", fmt.Sprintf(
errNonCanonicalVarInt, rv, discriminant, min)) errNonCanonicalVarInt, rv, discriminant, min))
} }
@ -365,7 +365,7 @@ func ReadVarInt(r io.Reader) (uint64, error) {
// encoded using fewer bytes. // encoded using fewer bytes.
min := uint64(0x10000) min := uint64(0x10000)
if rv < min { if rv < min {
return 0, messageError("ReadVarInt", fmt.Sprintf( return 0, messageError("readVarInt", fmt.Sprintf(
errNonCanonicalVarInt, rv, discriminant, min)) errNonCanonicalVarInt, rv, discriminant, min))
} }
@ -380,7 +380,7 @@ func ReadVarInt(r io.Reader) (uint64, error) {
// encoded using fewer bytes. // encoded using fewer bytes.
min := uint64(0xfd) min := uint64(0xfd)
if rv < min { if rv < min {
return 0, messageError("ReadVarInt", fmt.Sprintf( return 0, messageError("readVarInt", fmt.Sprintf(
errNonCanonicalVarInt, rv, discriminant, min)) errNonCanonicalVarInt, rv, discriminant, min))
} }

View File

@ -227,7 +227,7 @@ func TestElementWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := WriteElement(w, test.in) err := WriteElement(w, test.in)
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("writeElement #%d wrong error got: %v, want: %v", t.Errorf("writeElement #%d wrong error got: %v, want: %v",
i, err, test.writeErr) i, err, test.writeErr)
continue continue
@ -240,7 +240,7 @@ func TestElementWireErrors(t *testing.T) {
val = reflect.New(reflect.TypeOf(test.in)).Interface() val = reflect.New(reflect.TypeOf(test.in)).Interface()
} }
err = ReadElement(r, val) err = ReadElement(r, val)
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("readElement #%d wrong error got: %v, want: %v", t.Errorf("readElement #%d wrong error got: %v, want: %v",
i, err, test.readErr) i, err, test.readErr)
continue continue
@ -251,31 +251,30 @@ func TestElementWireErrors(t *testing.T) {
// TestVarIntWire tests wire encode and decode for variable length integers. // TestVarIntWire tests wire encode and decode for variable length integers.
func TestVarIntWire(t *testing.T) { func TestVarIntWire(t *testing.T) {
tests := []struct { tests := []struct {
in uint64 // Value to encode value uint64 // Value to encode
out uint64 // Expected decoded value
buf []byte // Wire encoding buf []byte // Wire encoding
}{ }{
// Latest protocol version. // Latest protocol version.
// Single byte // Single byte
{0, 0, []byte{0x00}}, {0, []byte{0x00}},
// Max single byte // Max single byte
{0xfc, 0xfc, []byte{0xfc}}, {0xfc, []byte{0xfc}},
// Min 2-byte // Min 2-byte
{0xfd, 0xfd, []byte{0xfd, 0x0fd, 0x00}}, {0xfd, []byte{0xfd, 0x0fd, 0x00}},
// Max 2-byte // Max 2-byte
{0xffff, 0xffff, []byte{0xfd, 0xff, 0xff}}, {0xffff, []byte{0xfd, 0xff, 0xff}},
// Min 4-byte // Min 4-byte
{0x10000, 0x10000, []byte{0xfe, 0x00, 0x00, 0x01, 0x00}}, {0x10000, []byte{0xfe, 0x00, 0x00, 0x01, 0x00}},
// Max 4-byte // Max 4-byte
{0xffffffff, 0xffffffff, []byte{0xfe, 0xff, 0xff, 0xff, 0xff}}, {0xffffffff, []byte{0xfe, 0xff, 0xff, 0xff, 0xff}},
// Min 8-byte // Min 8-byte
{ {
0x100000000, 0x100000000, 0x100000000,
[]byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00}, []byte{0xff, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00},
}, },
// Max 8-byte // Max 8-byte
{ {
0xffffffffffffffff, 0xffffffffffffffff, 0xffffffffffffffff,
[]byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff}, []byte{0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
}, },
} }
@ -283,8 +282,8 @@ func TestVarIntWire(t *testing.T) {
t.Logf("Running %d tests", len(tests)) t.Logf("Running %d tests", len(tests))
for i, test := range tests { for i, test := range tests {
// Encode to wire format. // Encode to wire format.
var buf bytes.Buffer buf := &bytes.Buffer{}
err := WriteVarInt(&buf, test.in) err := WriteVarInt(buf, test.value)
if err != nil { if err != nil {
t.Errorf("WriteVarInt #%d error %v", i, err) t.Errorf("WriteVarInt #%d error %v", i, err)
continue continue
@ -302,9 +301,9 @@ func TestVarIntWire(t *testing.T) {
t.Errorf("ReadVarInt #%d error %v", i, err) t.Errorf("ReadVarInt #%d error %v", i, err)
continue continue
} }
if val != test.out { if val != test.value {
t.Errorf("ReadVarInt #%d\n got: %d want: %d", i, t.Errorf("ReadVarInt #%d\n got: %x want: %x", i,
val, test.out) val, test.value)
continue continue
} }
} }
@ -338,7 +337,7 @@ func TestVarIntWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := WriteVarInt(w, test.in) err := WriteVarInt(w, test.in)
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("WriteVarInt #%d wrong error got: %v, want: %v", t.Errorf("WriteVarInt #%d wrong error got: %v, want: %v",
i, err, test.writeErr) i, err, test.writeErr)
continue continue
@ -347,7 +346,7 @@ func TestVarIntWireErrors(t *testing.T) {
// Decode from wire format. // Decode from wire format.
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
_, err = ReadVarInt(r) _, err = ReadVarInt(r)
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("ReadVarInt #%d wrong error got: %v, want: %v", t.Errorf("ReadVarInt #%d wrong error got: %v, want: %v",
i, err, test.readErr) i, err, test.readErr)
continue continue
@ -528,7 +527,7 @@ func TestVarStringWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := WriteVarString(w, test.in) err := WriteVarString(w, test.in)
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("WriteVarString #%d wrong error got: %v, want: %v", t.Errorf("WriteVarString #%d wrong error got: %v, want: %v",
i, err, test.writeErr) i, err, test.writeErr)
continue continue
@ -537,7 +536,7 @@ func TestVarStringWireErrors(t *testing.T) {
// Decode from wire format. // Decode from wire format.
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
_, err = ReadVarString(r, test.pver) _, err = ReadVarString(r, test.pver)
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("ReadVarString #%d wrong error got: %v, want: %v", t.Errorf("ReadVarString #%d wrong error got: %v, want: %v",
i, err, test.readErr) i, err, test.readErr)
continue continue
@ -659,7 +658,7 @@ func TestVarBytesWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := WriteVarBytes(w, test.pver, test.in) err := WriteVarBytes(w, test.pver, test.in)
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("WriteVarBytes #%d wrong error got: %v, want: %v", t.Errorf("WriteVarBytes #%d wrong error got: %v, want: %v",
i, err, test.writeErr) i, err, test.writeErr)
continue continue
@ -669,7 +668,7 @@ func TestVarBytesWireErrors(t *testing.T) {
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
_, err = ReadVarBytes(r, test.pver, MaxMessagePayload, _, err = ReadVarBytes(r, test.pver, MaxMessagePayload,
"test payload") "test payload")
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("ReadVarBytes #%d wrong error got: %v, want: %v", t.Errorf("ReadVarBytes #%d wrong error got: %v, want: %v",
i, err, test.readErr) i, err, test.readErr)
continue continue

View File

@ -341,11 +341,6 @@ func TestReadMessageWireErrors(t *testing.T) {
// Decode from wire format. // Decode from wire format.
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
nr, _, _, err := ReadMessageN(r, test.pver, test.kaspaNet) nr, _, _, err := ReadMessageN(r, test.pver, test.kaspaNet)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("ReadMessage #%d wrong error got: %v <%T>, "+
"want: %T", i, err, err, test.readErr)
continue
}
// Ensure the number of bytes written match the expected value. // Ensure the number of bytes written match the expected value.
if nr != test.bytes { if nr != test.bytes {
@ -354,14 +349,19 @@ func TestReadMessageWireErrors(t *testing.T) {
} }
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("ReadMessage #%d wrong error got: %v <%T>, "+ t.Errorf("ReadMessage #%d wrong error got: %v <%T>, "+
"want: %v <%T>", i, err, err, "want: %v <%T>", i, err, err,
test.readErr, test.readErr) test.readErr, test.readErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.readErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.readErr)
continue
} }
} }
} }
@ -432,7 +432,8 @@ func TestWriteMessageWireErrors(t *testing.T) {
} }
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.err { if err != test.err {
t.Errorf("ReadMessage #%d wrong error got: %v <%T>, "+ t.Errorf("ReadMessage #%d wrong error got: %v <%T>, "+

View File

@ -274,40 +274,40 @@ func TestAddrWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.KaspaEncode(w, test.pver) err := test.in.KaspaEncode(w, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, "+ t.Errorf("KaspaEncode #%d wrong error got: %v, "+
"want: %v", i, err, test.writeErr) "want: %v", i, err, test.writeErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.writeErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.writeErr)
continue
} }
// Decode from wire format. // Decode from wire format.
var msg MsgAddr var msg MsgAddr
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = msg.KaspaDecode(r, test.pver) err = msg.KaspaDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, "+ t.Errorf("KaspaDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr) "want: %v", i, err, test.readErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.readErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.readErr)
continue
} }
} }

View File

@ -6,6 +6,7 @@ package wire
import ( import (
"bytes" "bytes"
"github.com/pkg/errors"
"io" "io"
"math" "math"
"reflect" "reflect"
@ -239,7 +240,7 @@ func TestBlockWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.KaspaEncode(w, test.pver) err := test.in.KaspaEncode(w, test.pver)
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v", t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr) i, err, test.writeErr)
continue continue
@ -249,7 +250,7 @@ func TestBlockWireErrors(t *testing.T) {
var msg MsgBlock var msg MsgBlock
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = msg.KaspaDecode(r, test.pver) err = msg.KaspaDecode(r, test.pver)
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v", t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr) i, err, test.readErr)
continue continue
@ -365,7 +366,7 @@ func TestBlockSerializeErrors(t *testing.T) {
// Serialize the block. // Serialize the block.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.Serialize(w) err := test.in.Serialize(w)
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("Serialize #%d wrong error got: %v, want: %v", t.Errorf("Serialize #%d wrong error got: %v, want: %v",
i, err, test.writeErr) i, err, test.writeErr)
continue continue
@ -375,7 +376,7 @@ func TestBlockSerializeErrors(t *testing.T) {
var block MsgBlock var block MsgBlock
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = block.Deserialize(r) err = block.Deserialize(r)
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("Deserialize #%d wrong error got: %v, want: %v", t.Errorf("Deserialize #%d wrong error got: %v, want: %v",
i, err, test.readErr) i, err, test.readErr)
continue continue
@ -384,7 +385,7 @@ func TestBlockSerializeErrors(t *testing.T) {
var txLocBlock MsgBlock var txLocBlock MsgBlock
br := bytes.NewBuffer(test.buf[0:test.max]) br := bytes.NewBuffer(test.buf[0:test.max])
_, err = txLocBlock.DeserializeTxLoc(br) _, err = txLocBlock.DeserializeTxLoc(br)
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("DeserializeTxLoc #%d wrong error got: %v, want: %v", t.Errorf("DeserializeTxLoc #%d wrong error got: %v, want: %v",
i, err, test.readErr) i, err, test.readErr)
continue continue

View File

@ -225,40 +225,40 @@ func TestBlockLocatorWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.KaspaEncode(w, test.pver) err := test.in.KaspaEncode(w, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, "+ t.Errorf("KaspaEncode #%d wrong error got: %v, "+
"want: %v", i, err, test.writeErr) "want: %v", i, err, test.writeErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.writeErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.writeErr)
continue
} }
// Decode from wire format. // Decode from wire format.
var msg MsgBlockLocator var msg MsgBlockLocator
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = msg.KaspaDecode(r, test.pver) err = msg.KaspaDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, "+ t.Errorf("KaspaDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr) "want: %v", i, err, test.readErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.readErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.readErr)
continue
} }
} }
} }

View File

@ -139,40 +139,40 @@ func TestFeeFilterWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.KaspaEncode(w, test.pver) err := test.in.KaspaEncode(w, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, "+ t.Errorf("KaspaEncode #%d wrong error got: %v, "+
"want: %v", i, err, test.writeErr) "want: %v", i, err, test.writeErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.writeErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.writeErr)
continue
} }
// Decode from wire format. // Decode from wire format.
var msg MsgFeeFilter var msg MsgFeeFilter
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = msg.KaspaDecode(r, test.pver) err = msg.KaspaDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, "+ t.Errorf("KaspaDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr) "want: %v", i, err, test.readErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.readErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.readErr)
continue
} }
} }

View File

@ -119,40 +119,39 @@ func TestFilterAddWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.KaspaEncode(w, test.pver) err := test.in.KaspaEncode(w, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, "+ t.Errorf("KaspaEncode #%d wrong error got: %v, "+
"want: %v", i, err, test.writeErr) "want: %v", i, err, test.writeErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.writeErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.writeErr)
continue
} }
// Decode from wire format. // Decode from wire format.
var msg MsgFilterAdd var msg MsgFilterAdd
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = msg.KaspaDecode(r, test.pver) err = msg.KaspaDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, "+ t.Errorf("KaspaDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr) "want: %v", i, err, test.readErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.readErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.readErr)
continue
} }
} }
} }

View File

@ -165,40 +165,40 @@ func TestFilterLoadWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.KaspaEncode(w, test.pver) err := test.in.KaspaEncode(w, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, "+ t.Errorf("KaspaEncode #%d wrong error got: %v, "+
"want: %v", i, err, test.writeErr) "want: %v", i, err, test.writeErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.writeErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.writeErr)
continue
} }
// Decode from wire format. // Decode from wire format.
var msg MsgFilterLoad var msg MsgFilterLoad
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = msg.KaspaDecode(r, test.pver) err = msg.KaspaDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, "+ t.Errorf("KaspaDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr) "want: %v", i, err, test.readErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.readErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.readErr)
continue
} }
} }

View File

@ -207,7 +207,8 @@ func TestGetBlockInvsWireErrors(t *testing.T) {
} }
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.writeErr { if err != test.writeErr {
t.Errorf("KaspaEncode #%d wrong error got: %v, "+ t.Errorf("KaspaEncode #%d wrong error got: %v, "+
@ -227,7 +228,8 @@ func TestGetBlockInvsWireErrors(t *testing.T) {
} }
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.readErr { if err != test.readErr {
t.Errorf("KaspaDecode #%d wrong error got: %v, "+ t.Errorf("KaspaDecode #%d wrong error got: %v, "+

View File

@ -190,7 +190,8 @@ func TestGetBlockLocatorWireErrors(t *testing.T) {
} }
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.writeErr { if err != test.writeErr {
t.Errorf("KaspaEncode #%d wrong error got: %v, "+ t.Errorf("KaspaEncode #%d wrong error got: %v, "+
@ -210,7 +211,8 @@ func TestGetBlockLocatorWireErrors(t *testing.T) {
} }
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.readErr { if err != test.readErr {
t.Errorf("KaspaDecode #%d wrong error got: %v, "+ t.Errorf("KaspaDecode #%d wrong error got: %v, "+

View File

@ -231,40 +231,40 @@ func TestGetDataWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.KaspaEncode(w, test.pver) err := test.in.KaspaEncode(w, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, "+ t.Errorf("KaspaEncode #%d wrong error got: %v, "+
"want: %v", i, err, test.writeErr) "want: %v", i, err, test.writeErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.writeErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.writeErr)
continue
} }
// Decode from wire format. // Decode from wire format.
var msg MsgGetData var msg MsgGetData
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = msg.KaspaDecode(r, test.pver) err = msg.KaspaDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, "+ t.Errorf("KaspaDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr) "want: %v", i, err, test.readErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.readErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.readErr)
continue
} }
} }
} }

View File

@ -231,40 +231,40 @@ func TestInvWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.KaspaEncode(w, test.pver) err := test.in.KaspaEncode(w, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, "+ t.Errorf("KaspaEncode #%d wrong error got: %v, "+
"want: %v", i, err, test.writeErr) "want: %v", i, err, test.writeErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.writeErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.writeErr)
continue
} }
// Decode from wire format. // Decode from wire format.
var msg MsgInv var msg MsgInv
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = msg.KaspaDecode(r, test.pver) err = msg.KaspaDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, "+ t.Errorf("KaspaDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr) "want: %v", i, err, test.readErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.readErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.readErr)
continue
} }
} }

View File

@ -230,40 +230,40 @@ func TestMerkleBlockWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.KaspaEncode(w, test.pver) err := test.in.KaspaEncode(w, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, "+ t.Errorf("KaspaEncode #%d wrong error got: %v, "+
"want: %v", i, err, test.writeErr) "want: %v", i, err, test.writeErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.writeErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.writeErr)
continue
} }
// Decode from wire format. // Decode from wire format.
var msg MsgMerkleBlock var msg MsgMerkleBlock
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = msg.KaspaDecode(r, test.pver) err = msg.KaspaDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, "+ t.Errorf("KaspaDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr) "want: %v", i, err, test.readErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.readErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.readErr)
continue
} }
} }
} }

View File

@ -221,40 +221,39 @@ func TestNotFoundWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.KaspaEncode(w, test.pver) err := test.in.KaspaEncode(w, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, "+ t.Errorf("KaspaEncode #%d wrong error got: %v, "+
"want: %v", i, err, test.writeErr) "want: %v", i, err, test.writeErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.writeErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.writeErr)
continue
} }
// Decode from wire format. // Decode from wire format.
var msg MsgNotFound var msg MsgNotFound
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = msg.KaspaDecode(r, test.pver) err = msg.KaspaDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, "+ t.Errorf("KaspaDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr) "want: %v", i, err, test.readErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.readErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.readErr)
continue
} }
} }
} }

View File

@ -6,6 +6,7 @@ package wire
import ( import (
"bytes" "bytes"
"github.com/pkg/errors"
"io" "io"
"reflect" "reflect"
"testing" "testing"
@ -145,7 +146,7 @@ func TestPingWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.KaspaEncode(w, test.pver) err := test.in.KaspaEncode(w, test.pver)
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v", t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr) i, err, test.writeErr)
continue continue
@ -155,7 +156,7 @@ func TestPingWireErrors(t *testing.T) {
var msg MsgPing var msg MsgPing
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = msg.KaspaDecode(r, test.pver) err = msg.KaspaDecode(r, test.pver)
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v", t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr) i, err, test.readErr)
continue continue

View File

@ -162,40 +162,40 @@ func TestPongWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.KaspaEncode(w, test.pver) err := test.in.KaspaEncode(w, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, "+ t.Errorf("KaspaEncode #%d wrong error got: %v, "+
"want: %v", i, err, test.writeErr) "want: %v", i, err, test.writeErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.writeErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.writeErr)
continue
} }
// Decode from wire format. // Decode from wire format.
var msg MsgPong var msg MsgPong
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = msg.KaspaDecode(r, test.pver) err = msg.KaspaDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, "+ t.Errorf("KaspaDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr) "want: %v", i, err, test.readErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.readErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.readErr)
continue
} }
} }

View File

@ -258,40 +258,40 @@ func TestRejectWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.KaspaEncode(w, test.pver) err := test.in.KaspaEncode(w, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, "+ t.Errorf("KaspaEncode #%d wrong error got: %v, "+
"want: %v", i, err, test.writeErr) "want: %v", i, err, test.writeErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.writeErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.writeErr)
continue
} }
// Decode from wire format. // Decode from wire format.
var msg MsgReject var msg MsgReject
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = msg.KaspaDecode(r, test.pver) err = msg.KaspaDecode(r, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, "+ t.Errorf("KaspaDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr) "want: %v", i, err, test.readErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.readErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.readErr)
continue
} }
} }
} }

View File

@ -7,6 +7,7 @@ package wire
import ( import (
"bytes" "bytes"
"fmt" "fmt"
"github.com/pkg/errors"
"io" "io"
"math" "math"
"reflect" "reflect"
@ -365,7 +366,7 @@ func TestTxWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.KaspaEncode(w, test.pver) err := test.in.KaspaEncode(w, test.pver)
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v", t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr) i, err, test.writeErr)
continue continue
@ -375,7 +376,7 @@ func TestTxWireErrors(t *testing.T) {
var msg MsgTx var msg MsgTx
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = msg.KaspaDecode(r, test.pver) err = msg.KaspaDecode(r, test.pver)
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v", t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr) i, err, test.readErr)
continue continue
@ -568,7 +569,7 @@ func TestTxSerializeErrors(t *testing.T) {
// Serialize the transaction. // Serialize the transaction.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.Serialize(w) err := test.in.Serialize(w)
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("Serialize #%d wrong error got: %v, want: %v", t.Errorf("Serialize #%d wrong error got: %v, want: %v",
i, err, test.writeErr) i, err, test.writeErr)
continue continue
@ -578,7 +579,7 @@ func TestTxSerializeErrors(t *testing.T) {
var tx MsgTx var tx MsgTx
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = tx.Deserialize(r) err = tx.Deserialize(r)
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("Deserialize #%d wrong error got: %v, want: %v", t.Errorf("Deserialize #%d wrong error got: %v, want: %v",
i, err, test.readErr) i, err, test.readErr)
continue continue

View File

@ -267,40 +267,40 @@ func TestVersionWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := test.in.KaspaEncode(w, test.pver) err := test.in.KaspaEncode(w, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, want: %v",
i, err, test.writeErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("KaspaEncode #%d wrong error got: %v, "+ t.Errorf("KaspaEncode #%d wrong error got: %v, "+
"want: %v", i, err, test.writeErr) "want: %v", i, err, test.writeErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.writeErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.writeErr)
continue
} }
// Decode from wire format. // Decode from wire format.
var msg MsgVersion var msg MsgVersion
buf := bytes.NewBuffer(test.buf[0:test.max]) buf := bytes.NewBuffer(test.buf[0:test.max])
err = msg.KaspaDecode(buf, test.pver) err = msg.KaspaDecode(buf, test.pver)
if reflect.TypeOf(err) != reflect.TypeOf(test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, want: %v",
i, err, test.readErr)
continue
}
// For errors which are not of type MessageError, check them for // For errors which are not of type MessageError, check them for
// equality. // equality. If the error is a MessageError, check only if it's
// the expected type.
if msgErr := &(MessageError{}); !errors.As(err, &msgErr) { if msgErr := &(MessageError{}); !errors.As(err, &msgErr) {
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("KaspaDecode #%d wrong error got: %v, "+ t.Errorf("KaspaDecode #%d wrong error got: %v, "+
"want: %v", i, err, test.readErr) "want: %v", i, err, test.readErr)
continue continue
} }
} else if reflect.TypeOf(msgErr) != reflect.TypeOf(test.readErr) {
t.Errorf("ReadMessage #%d wrong error type got: %T, "+
"want: %T", i, msgErr, test.readErr)
continue
} }
} }
} }

View File

@ -6,6 +6,7 @@ package wire
import ( import (
"bytes" "bytes"
"github.com/pkg/errors"
"io" "io"
"net" "net"
"reflect" "reflect"
@ -198,7 +199,7 @@ func TestNetAddressWireErrors(t *testing.T) {
// Encode to wire format. // Encode to wire format.
w := newFixedWriter(test.max) w := newFixedWriter(test.max)
err := writeNetAddress(w, test.pver, test.in, test.ts) err := writeNetAddress(w, test.pver, test.in, test.ts)
if err != test.writeErr { if !errors.Is(err, test.writeErr) {
t.Errorf("writeNetAddress #%d wrong error got: %v, want: %v", t.Errorf("writeNetAddress #%d wrong error got: %v, want: %v",
i, err, test.writeErr) i, err, test.writeErr)
continue continue
@ -208,7 +209,7 @@ func TestNetAddressWireErrors(t *testing.T) {
var na NetAddress var na NetAddress
r := newFixedReader(test.max, test.buf) r := newFixedReader(test.max, test.buf)
err = readNetAddress(r, test.pver, &na, test.ts) err = readNetAddress(r, test.pver, &na, test.ts)
if err != test.readErr { if !errors.Is(err, test.readErr) {
t.Errorf("readNetAddress #%d wrong error got: %v, want: %v", t.Errorf("readNetAddress #%d wrong error got: %v, want: %v",
i, err, test.readErr) i, err, test.readErr)
continue continue