181 lines
4.2 KiB
Go
181 lines
4.2 KiB
Go
// Copyright (c) 2013 Conformal Systems LLC.
|
|
// Use of this source code is governed by an ISC
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package btcwire
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"crypto/sha256"
|
|
"encoding/binary"
|
|
"io"
|
|
"math"
|
|
)
|
|
|
|
// Maximum payload size for a variable length integer.
|
|
const maxVarIntPayload = 9
|
|
|
|
// readElement reads the next sequence of bytes from r using little endian
|
|
// depending on the concrete type of element pointed to.
|
|
func readElement(r io.Reader, element interface{}) error {
|
|
return binary.Read(r, binary.LittleEndian, element)
|
|
}
|
|
|
|
// readElements reads multiple items from r. It is equivalent to multiple
|
|
// calls to readElement.
|
|
func readElements(r io.Reader, elements ...interface{}) error {
|
|
for _, element := range elements {
|
|
err := readElement(r, element)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// writeElement writes the little endian representation of element to w.
|
|
func writeElement(w io.Writer, element interface{}) error {
|
|
return binary.Write(w, binary.LittleEndian, element)
|
|
}
|
|
|
|
// writeElements writes multiple items to w. It is equivalent to multiple
|
|
// calls to writeElement.
|
|
func writeElements(w io.Writer, elements ...interface{}) error {
|
|
for _, element := range elements {
|
|
err := writeElement(w, element)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// readVarInt reads a variable length integer from r and returns it as a uint64.
|
|
func readVarInt(r io.Reader, pver uint32) (uint64, error) {
|
|
b := make([]byte, 1)
|
|
_, err := r.Read(b)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
var rv uint64
|
|
discriminant := uint8(b[0])
|
|
switch discriminant {
|
|
case 0xff:
|
|
var u uint64
|
|
err = binary.Read(r, binary.LittleEndian, &u)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
rv = u
|
|
|
|
case 0xfe:
|
|
var u uint32
|
|
err = binary.Read(r, binary.LittleEndian, &u)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
rv = uint64(u)
|
|
|
|
case 0xfd:
|
|
var u uint16
|
|
err = binary.Read(r, binary.LittleEndian, &u)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
rv = uint64(u)
|
|
|
|
default:
|
|
rv = uint64(discriminant)
|
|
}
|
|
|
|
return rv, nil
|
|
}
|
|
|
|
// writeVarInt serializes val to w using a variable number of bytes depending
|
|
// on its value.
|
|
func writeVarInt(w io.Writer, pver uint32, val uint64) error {
|
|
if val > math.MaxUint32 {
|
|
err := writeElements(w, []byte{0xff}, uint64(val))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
if val > math.MaxUint16 {
|
|
err := writeElements(w, []byte{0xfe}, uint32(val))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
if val >= 0xfd {
|
|
err := writeElements(w, []byte{0xfd}, uint16(val))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
return writeElement(w, uint8(val))
|
|
}
|
|
|
|
// readVarString reads a variable length string from r and returns it as a Go
|
|
// string. A varString is encoded as a varInt containing the length of the
|
|
// string, and the bytes that represent the string itself.
|
|
func readVarString(r io.Reader, pver uint32) (string, error) {
|
|
slen, err := readVarInt(r, pver)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
buf := make([]byte, slen)
|
|
err = readElement(r, buf)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(buf), nil
|
|
}
|
|
|
|
// writeVarString serializes str to w as a varInt containing the length of the
|
|
// string followed by the bytes that represent the string itself.
|
|
func writeVarString(w io.Writer, pver uint32, str string) error {
|
|
err := writeVarInt(w, pver, uint64(len(str)))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
err = writeElement(w, []byte(str))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// randomUint64 returns a cryptographically random uint64 value. This
|
|
// unexported version takes a reader primarily to ensure the error paths
|
|
// can be properly by passing a fake reader in the tests.
|
|
func randomUint64(r io.Reader) (uint64, error) {
|
|
b := make([]byte, 8)
|
|
n, err := r.Read(b)
|
|
if n != len(b) {
|
|
return 0, io.ErrShortBuffer
|
|
}
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
return binary.BigEndian.Uint64(b), nil
|
|
}
|
|
|
|
// RandomUint64 returns a cryptographically random uint64 value.
|
|
func RandomUint64() (uint64, error) {
|
|
return randomUint64(rand.Reader)
|
|
}
|
|
|
|
// DoubleSha256 calculates sha256(sha256(b)) and returns the resulting bytes.
|
|
func DoubleSha256(b []byte) []byte {
|
|
hasher := sha256.New()
|
|
hasher.Write(b)
|
|
sum := hasher.Sum(nil)
|
|
hasher.Reset()
|
|
hasher.Write(sum)
|
|
sum = hasher.Sum(nil)
|
|
return sum
|
|
}
|