pool/sidecar/codec.go
Olaoluwa Osuntokun 2a83726c21
sidecar: switch sidecar encoding from base68 to base58
In this commit, we change the encoding of the sidecar ticket from base68
to base58 as it doesn't contain any special characters which makes copy
pasting a string easier.

An example encoding of the new string looks something like:
```
sidecar13HtPsD1UiB2EA4ZfUbnVejG1Pu3p1GW2ydq65NZBuQgTaD9LRESHtKasXwNrQPYqkwkMyJNFiNoBig4PBQQKoKeXz5jHWXZ3pMZNX5JW2FXyf6zKTPWthRQczNQuf2pM8ppjTqHK6tYTEReZk7pFynYJoHnAeBtix28k2PiRSz5qVVmHpWHWFgVjxihEmcD1T1V1g5LwzVSRUyCJ11ajaE4nHdnU1ZR3HdduxXnyyefacfXWAts3tytr7RnWzRbnVPFSx7eVgeinknYvQ3njtiStpQpSacoBH4y1KckVpRAo8N8KuWWmAgYBNpxZfK8RsgViJ26kTpbfuyyHy1uPkAsGtv25iz5pNcopXyaXMnZGyDDWrDf9ZJEgU6wzm4VxwrPfNN4ASR6wLGWAKy1SsCMVFi5Bx6khN4ReSmJVrALWXrbgM52j7evHVRYXa1LZCJTBccCvaRXNYQqxJvnUnLnHW4N4THv4j1y94wmo2SafRfKapxppVW
```
2021-05-19 19:55:56 -07:00

127 lines
4.2 KiB
Go

package sidecar
import (
"bytes"
"crypto/sha256"
"fmt"
"github.com/btcsuite/btcutil/base58"
)
const (
// checksumLen is the number of bytes we add as a checksum. We'll take
// this many bytes out of the full SHA256 hash and add it to the string
// encoded version of the ticket as an additional integrity check.
checksumLen = 4
// sidecarPrefix will serve as our "human readable" prefix to make the
// strings easily identifiable.
sidecarPrefix = "sidecar"
)
var (
// encodingVersion is the current encoding version used so each ticket
// will have a predictable character after the sidecar prefix.
encodingVersion = []byte{0}
)
// EncodeToString serializes and encodes the ticket as an URL safe string that
// contains a human readable prefix and a checksum.
func EncodeToString(t *Ticket) (string, error) {
var (
checksumBuf bytes.Buffer
encodeBuf bytes.Buffer
)
// First, we'll write the sidecar prefix, as well as the serialized
// ticket into the buffer that we'll use to generate the checksum. We
// do this as we won't encode the checksum using base58.
if _, err := checksumBuf.Write([]byte(sidecarPrefix)); err != nil {
return "", err
}
if _, err := checksumBuf.Write(encodingVersion); err != nil {
return "", err
}
if err := SerializeTicket(&checksumBuf, t); err != nil {
return "", err
}
// We'll now hash our full checksum buffer to generate the checksum
// that we'll tack onto the end of the ticket.
checksum := sha256.Sum256(checksumBuf.Bytes())
// Now that we have the checksum, we'll write the raw ticket and the
// checksum to the buffer that we'll use for encoding.
//
// Similar to Bitcoin, we'll add a version so the prefix of the string
// is always the same.
if _, err := encodeBuf.Write(encodingVersion); err != nil {
return "", err
}
if err := SerializeTicket(&encodeBuf, t); err != nil {
return "", err
}
if _, err := encodeBuf.Write(checksum[:checksumLen]); err != nil {
return "", err
}
// The final ticket is the sidecar encoding (non base58 encoded) with
// the base58 encoded ticket following it.
baseTicket := base58.Encode(encodeBuf.Bytes())
return sidecarPrefix + baseTicket, nil
}
// DecodeString decodes and then deserializes the given sidecar ticket string.
func DecodeString(s string) (*Ticket, error) {
prefixLength := len(sidecarPrefix)
if len(s) < prefixLength {
return nil, fmt.Errorf("string contains invalid prefix")
}
// First, we'll decode the set of raw bytes from the base58 encoding,
// snipping off our custom prefix before decoding.
rawBytes := base58.Decode(s[prefixLength:])
// We'll also ensure that the length passes basic sanity checks to not
// trip up the slicing logic below.
expectedLen := len(encodingVersion) + checksumLen
if len(rawBytes) < expectedLen {
return nil, fmt.Errorf("not a sidecar ticket, invalid "+
"length: %v vs %v", len(rawBytes), expectedLen)
}
// We'll ensure that the sidecar prefix matches exactly (of the string
// version), as otherwise the checksum check won't pass anyway.
encodedPrefix := s[:prefixLength]
if encodedPrefix != sidecarPrefix {
return nil, fmt.Errorf("not a sidecar ticket, invalid prefix "+
"%x", encodedPrefix)
}
// Now that we know the prefix passes, we'll move onto verifying the
// checksum.
totalLength := len(rawBytes)
versionLen := len(encodingVersion)
payloadLength := totalLength - checksumLen - len(encodingVersion)
// Extract the payload (between the prefix and the checksum), from the
// checksum itself (everything after the payload).
payload := rawBytes[versionLen : versionLen+payloadLength]
checksum := rawBytes[totalLength-checksumLen : totalLength]
// Calculate the SHA256 sum of the human readable prefix and payload
// and compare it to the checksum we also expect to be in the full
// string serialized ticket.
hash := sha256.New()
_, _ = hash.Write([]byte(sidecarPrefix))
_, _ = hash.Write(encodingVersion)
_, _ = hash.Write(payload)
calculatedChecksum := hash.Sum(nil)[:checksumLen]
if !bytes.Equal(checksum, calculatedChecksum) {
return nil, fmt.Errorf("invalid sidecar ticket, checksum " +
"mismatch")
}
// Everything's fine, let's decode the actual payload.
return DeserializeTicket(bytes.NewReader(payload))
}