package prefixes

// The prefixes package contains the key/value structures for the rocksdb prefix
// store and related functions.
// Each key/value prefix pair has a pack and unpack function for serializing / deserializing
// it into bytes for storage in rocksdb. They also have a pack partial function which allows
// for serialization of a set number of fields, this is used i.e. for keys with a hash, tx_num
// and height where there multiple be multiple with the same hash but different tx_num and height.

import (
	"bytes"
	"encoding/binary"
	"encoding/hex"
	"fmt"
	"log"
	"reflect"
	"sort"
	"strings"

	"github.com/lbryio/herald.go/internal"
	"github.com/lbryio/lbcd/chaincfg/chainhash"
)

const (
	ClaimToSupport = 'K'
	SupportToClaim = 'L'

	ClaimToTXO = 'E'
	TXOToClaim = 'G'

	ClaimToChannel = 'I'
	ChannelToClaim = 'J'

	ClaimShortIdPrefix = 'F'
	BidOrder           = 'D'
	ClaimExpiration    = 'O'

	ClaimTakeover            = 'P'
	PendingActivation        = 'Q'
	ActivatedClaimAndSupport = 'R'
	ActiveAmount             = 'S'

	Repost        = 'V'
	RepostedClaim = 'W'

	Undo      = 'M'
	ClaimDiff = 'Y'

	Tx            = 'B'
	BlockHash     = 'C'
	Header        = 'H'
	TxNum         = 'N'
	TxCount       = 'T'
	TxHash        = 'X'
	UTXO          = 'u'
	HashXUTXO     = 'h'
	HashXHistory  = 'x'
	DBState       = 's'
	ChannelCount  = 'Z'
	SupportAmount = 'a'
	BlockTXs      = 'b'

	TrendingNotifications = 'c'
	MempoolTx             = 'd'
	TouchedHashX          = 'e'
	HashXStatus           = 'f'
	HashXMempoolStatus    = 'g'

	EffectiveAmount = 'i'
	RepostedCount   = 'j'

	ActivateClaimTXOType    = 1
	ActivatedSupportTXOType = 2

	OnesCompTwiddle64 uint64 = 0xffffffffffffffff
	OnesCompTwiddle32 uint32 = 0xffffffff
)

// GetPrefixes returns an array of all the byte prefix constants.
func GetPrefixes() [][]byte {
	return [][]byte{
		{ClaimToSupport},
		{SupportToClaim},
		{ClaimToTXO},
		{TXOToClaim},
		{ClaimToChannel},
		{ChannelToClaim},
		{ClaimShortIdPrefix},
		{BidOrder},
		{ClaimExpiration},
		{ClaimTakeover},
		{PendingActivation},
		{ActivatedClaimAndSupport},
		{ActiveAmount},
		{Repost},
		{RepostedClaim},
		{Undo},
		{ClaimDiff},
		{Tx},
		{BlockHash},
		{Header},
		{TxNum},
		{TxCount},
		{TxHash},
		{UTXO},
		{HashXUTXO},
		{HashXHistory},
		{DBState},
		{ChannelCount},
		{SupportAmount},
		{BlockTXs},
		{TrendingNotifications},
		{MempoolTx},
		{TouchedHashX},
		{HashXStatus},
		{HashXMempoolStatus},
		{EffectiveAmount},
		{RepostedCount},
	}
}

// PrefixRowKV is a generic key/value pair for a prefix.
type PrefixRowKV struct {
	Key      BaseKey
	Value    BaseValue
	RawKey   []byte
	RawValue []byte
	Error    error
}

type BaseKey interface {
	NumFields() int
	PartialPack(fields int) []byte
	PackKey() []byte
}

type BaseValue interface {
	PackValue() []byte
}

type KeyUnpacker interface {
	UnpackKey(buf []byte)
}

type ValueUnpacker interface {
	UnpackValue(buf []byte)
}

type LengthEncodedName struct {
	NameLen uint16 `struct:"sizeof=Name"`
	Name    string `json:"name"`
}

func NewLengthEncodedName(s string) LengthEncodedName {
	return LengthEncodedName{
		NameLen: uint16(len(s)),
		Name:    s,
	}
}

type LengthEncodedNormalizedName struct {
	NormalizedNameLen uint16 `struct:"sizeof=NormalizedName"`
	NormalizedName    string `json:"normalized_name"`
}

func NewLengthEncodedNormalizedName(s string) LengthEncodedNormalizedName {
	return LengthEncodedNormalizedName{
		NormalizedNameLen: uint16(len(s)),
		NormalizedName:    s,
	}
}

type LengthEncodedPartialClaimId struct {
	PartialClaimIdLen uint8  `struct:"sizeof=PartialClaimId"`
	PartialClaimId    string `json:"partial_claim_id"`
}

func NewLengthEncodedPartialClaimId(s string) LengthEncodedPartialClaimId {
	return LengthEncodedPartialClaimId{
		PartialClaimIdLen: uint8(len(s)),
		PartialClaimId:    s,
	}
}

type DBStateKey struct {
	Prefix []byte `struct:"[1]byte" json:"prefix"`
}

type DBStateValue struct {
	Genesis        *chainhash.Hash
	Height         uint32
	TxCount        uint32
	Tip            *chainhash.Hash
	UtxoFlushCount uint32
	WallTime       uint32
	FirstSync      bool
	DBVersion      uint8
	HistFlushCount int32
	CompFlushCount int32
	CompCursor     int32
	EsSyncHeight   uint32
}

func NewDBStateValue() *DBStateValue {
	return &DBStateValue{
		Genesis:        new(chainhash.Hash),
		Height:         0,
		TxCount:        0,
		Tip:            new(chainhash.Hash),
		UtxoFlushCount: 0,
		WallTime:       0,
		FirstSync:      true,
		DBVersion:      0,
		HistFlushCount: 0,
		CompFlushCount: -1,
		CompCursor:     -1,
		EsSyncHeight:   0,
	}
}

func NewDBStateKey() *DBStateKey {
	return &DBStateKey{
		Prefix: []byte{DBState},
	}
}

func (k *DBStateKey) PackKey() []byte {
	prefixLen := 1
	n := prefixLen
	key := make([]byte, n)
	copy(key, k.Prefix)

	return key
}

func (v *DBStateValue) PackValue() []byte {
	// b'>32sLL32sLLBBlllL'
	n := 32 + 4 + 4 + 32 + 4 + 4 + 1 + 1 + 4 + 4 + 4 + 4
	value := make([]byte, n)
	copy(value, v.Genesis[:32])
	binary.BigEndian.PutUint32(value[32:], v.Height)
	binary.BigEndian.PutUint32(value[32+4:], v.TxCount)
	copy(value[32+4+4:], v.Tip[:32])
	binary.BigEndian.PutUint32(value[32+4+4+32:], v.UtxoFlushCount)
	binary.BigEndian.PutUint32(value[32+4+4+32+4:], v.WallTime)
	var bitSetVar uint8
	if v.FirstSync {
		bitSetVar = 1
	}
	value[32+4+4+32+4+4] = bitSetVar
	value[32+4+4+32+4+4+1] = v.DBVersion

	binary.BigEndian.PutUint32(value[32+4+4+32+4+4+1+1:], uint32(v.HistFlushCount))
	binary.BigEndian.PutUint32(value[32+4+4+32+4+4+1+1+4:], uint32(v.CompFlushCount))
	binary.BigEndian.PutUint32(value[32+4+4+32+4+4+1+1+4+4:], uint32(v.CompCursor))
	binary.BigEndian.PutUint32(value[32+4+4+32+4+4+1+1+4+4+4:], v.EsSyncHeight)

	return value
}

func (kv *DBStateKey) NumFields() int {
	return 0
}

func (k *DBStateKey) PartialPack(fields int) []byte {
	prefixLen := 1
	var n = prefixLen

	key := make([]byte, n)
	copy(key, k.Prefix)

	return key
}

func DBStateKeyUnpack(key []byte) *DBStateKey {
	prefixLen := 1
	return &DBStateKey{
		Prefix: key[:prefixLen],
	}
}

func DBStateValueUnpack(value []byte) *DBStateValue {
	genesis := (*chainhash.Hash)(value[:32])
	tip := (*chainhash.Hash)(value[32+4+4 : 32+4+4+32])
	x := &DBStateValue{
		Genesis:        genesis,
		Height:         binary.BigEndian.Uint32(value[32:]),
		TxCount:        binary.BigEndian.Uint32(value[32+4:]),
		Tip:            tip,
		UtxoFlushCount: binary.BigEndian.Uint32(value[32+4+4+32:]),
		WallTime:       binary.BigEndian.Uint32(value[32+4+4+32+4:]),
		FirstSync:      value[32+4+4+32+4+4] == 1,
		DBVersion:      value[32+4+4+32+4+4+1],
		HistFlushCount: int32(binary.BigEndian.Uint32(value[32+4+4+32+4+4+1+1:])),
		CompFlushCount: int32(binary.BigEndian.Uint32(value[32+4+4+32+4+4+1+1+4:])),
		CompCursor:     int32(binary.BigEndian.Uint32(value[32+4+4+32+4+4+1+1+4+4:])),
		EsSyncHeight:   binary.BigEndian.Uint32(value[32+4+4+32+4+4+1+1+4+4+4:]),
	}
	return x
}

type UndoKey struct {
	Prefix []byte `struct:"[1]byte" json:"prefix"`
	Height uint64 `json:"height"`
}

type UndoValue struct {
	Data []byte `struct-while:"!_eof" json:"data"`
}

func (k *UndoKey) PackKey() []byte {
	prefixLen := 1
	// b'>L'
	n := prefixLen + 8
	key := make([]byte, n)
	copy(key, k.Prefix)
	binary.BigEndian.PutUint64(key[prefixLen:], k.Height)

	return key
}

func (v *UndoValue) PackValue() []byte {
	len := len(v.Data)
	value := make([]byte, len)
	copy(value, v.Data)

	return value
}

func (kv *UndoKey) NumFields() int {
	return 1
}

func (k *UndoKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 1 {
		fields = 1
	}
	if fields < 0 {
		fields = 0
	}

	// b'>4sLH'
	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 8
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			binary.BigEndian.PutUint64(key[prefixLen:], k.Height)
		}
	}

	return key
}

func UndoKeyUnpack(key []byte) *UndoKey {
	prefixLen := 1
	return &UndoKey{
		Prefix: key[:prefixLen],
		Height: binary.BigEndian.Uint64(key[prefixLen:]),
	}
}

func UndoValueUnpack(value []byte) *UndoValue {
	return &UndoValue{
		Data: value,
	}
}

type UTXOKey struct {
	Prefix []byte `struct:"[1]byte" json:"prefix"`
	HashX  []byte `struct:"[11]byte" json:"hashx"`
	TxNum  uint32 `json:"tx_num"`
	Nout   uint16 `json:"nout"`
}

type UTXOValue struct {
	Amount uint64 `json:"amount"`
}

type HashXUTXOKey struct {
	Prefix      []byte `struct:"[1]byte" json:"prefix"`
	ShortTXHash []byte `struct:"[4]byte" json:"short_tx_hash"`
	TxNum       uint32 `json:"tx_num"`
	Nout        uint16 `json:"nout"`
}

type HashXUTXOValue struct {
	HashX []byte `struct:"[11]byte" json:"hashx"`
}

//
// HashXUTXOKey / HashXUTXOValue
//

func (k *HashXUTXOKey) String() string {
	return fmt.Sprintf(
		"%s(short_tx_hash=%s, tx_num=%d, nout=%d)",
		reflect.TypeOf(k),
		hex.EncodeToString(k.ShortTXHash),
		k.TxNum,
		k.Nout,
	)
}

func (v *HashXUTXOValue) String() string {
	return fmt.Sprintf(
		"%s(hashX=%s)",
		reflect.TypeOf(v),
		hex.EncodeToString(v.HashX),
	)
}

func (k *HashXUTXOKey) PackKey() []byte {
	prefixLen := 1
	// b'>4sLH'
	n := prefixLen + 4 + 4 + 2
	key := make([]byte, n)
	copy(key, k.Prefix)
	copy(key[prefixLen:], k.ShortTXHash)
	binary.BigEndian.PutUint32(key[prefixLen+4:], k.TxNum)
	binary.BigEndian.PutUint16(key[prefixLen+8:], k.Nout)

	return key
}

func (v *HashXUTXOValue) PackValue() []byte {
	value := make([]byte, 11)
	copy(value, v.HashX)

	return value
}

func (kv *HashXUTXOKey) NumFields() int {
	return 3
}

// HashXUTXOKeyPackPartial packs a variable number of fields into a byte
// array
func (k *HashXUTXOKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 3 {
		fields = 3
	}
	if fields < 0 {
		fields = 0
	}

	// b'>4sLH'
	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 4
		case 2:
			n += 4
		case 3:
			n += 2
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			copy(key[prefixLen:], k.ShortTXHash)
		case 2:
			binary.BigEndian.PutUint32(key[prefixLen+4:], k.TxNum)
		case 3:
			binary.BigEndian.PutUint16(key[prefixLen+8:], k.Nout)
		}
	}

	return key
}

func HashXUTXOKeyUnpack(key []byte) *HashXUTXOKey {
	prefixLen := 1
	return &HashXUTXOKey{
		Prefix:      key[:prefixLen],
		ShortTXHash: key[prefixLen : prefixLen+4],
		TxNum:       binary.BigEndian.Uint32(key[prefixLen+4:]),
		Nout:        binary.BigEndian.Uint16(key[prefixLen+8:]),
	}
}

func HashXUTXOValueUnpack(value []byte) *HashXUTXOValue {
	return &HashXUTXOValue{
		HashX: value[:11],
	}
}

type HashXHistoryKey struct {
	Prefix []byte `struct:"[1]byte" json:"prefix"`
	HashX  []byte `struct:"[11]byte" json:"hashx"`
	Height uint32 `json:"height"`
}

type HashXHistoryValue struct {
	TxNums []uint32 `struct:"lsb" struct-while:"!_eof" json:"tx_nums"`
}

func (k *HashXHistoryKey) String() string {
	return fmt.Sprintf(
		"%s(hashx=%s, height=%d)",
		reflect.TypeOf(k),
		hex.EncodeToString(k.HashX),
		k.Height,
	)
}

func (k *HashXHistoryKey) PackKey() []byte {
	prefixLen := 1
	// b'>11sL'
	n := prefixLen + 11 + 4
	key := make([]byte, n)
	copy(key, k.Prefix)
	copy(key[prefixLen:], k.HashX)
	binary.BigEndian.PutUint32(key[prefixLen+11:], k.Height)

	return key
}

func (v *HashXHistoryValue) PackValue() []byte {
	n := len(v.TxNums)
	value := make([]byte, n*4)
	for i, x := range v.TxNums {
		binary.LittleEndian.PutUint32(value[i*4:], x)
	}

	return value
}

func (kv *HashXHistoryKey) NumFields() int {
	return 2
}

// HashXHistoryKeyPackPartial packs a variable number of fields into a byte
// array
func (k *HashXHistoryKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 2 {
		fields = 2
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 11
		case 2:
			n += 4
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			copy(key[prefixLen:], k.HashX[:11])
		case 2:
			binary.BigEndian.PutUint32(key[prefixLen+11:], k.Height)
		}
	}

	return key
}

func HashXHistoryKeyUnpack(key []byte) *HashXHistoryKey {
	prefixLen := 1
	return &HashXHistoryKey{
		Prefix: key[:prefixLen],
		HashX:  key[prefixLen : prefixLen+11],
		Height: binary.BigEndian.Uint32(key[prefixLen+11:]),
	}
}

func HashXHistoryValueUnpack(value []byte) *HashXHistoryValue {
	n := len(value) / 4
	txnums := make([]uint32, n)
	for i := 0; i < n; i++ {
		txnums[i] = binary.LittleEndian.Uint32(value[i*4:])
	}
	return &HashXHistoryValue{
		TxNums: txnums,
	}
}

type BlockHashKey struct {
	Prefix []byte `struct:"[1]byte" json:"prefix"`
	Height uint32 `json:"height"`
}

type BlockHashValue struct {
	BlockHash *chainhash.Hash `json:"block_hash"`
}

func NewBlockHashKey(height uint32) *BlockHashKey {
	return &BlockHashKey{
		Prefix: []byte{BlockHash},
		Height: height,
	}
}

func (k *BlockHashKey) PackKey() []byte {
	prefixLen := 1
	// b'>L'
	n := prefixLen + 4
	key := make([]byte, n)
	copy(key, k.Prefix)
	binary.BigEndian.PutUint32(key[prefixLen:], k.Height)

	return key
}

func (v *BlockHashValue) PackValue() []byte {
	value := make([]byte, 32)
	copy(value, v.BlockHash[:32])

	return value
}

func (kv *BlockHashKey) NumFields() int {
	return 1
}

func (k *BlockHashKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 1 {
		fields = 1
	}
	if fields < 0 {
		fields = 0
	}

	// b'>4sLH'
	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 4
		case 2:
			n += 4
		case 3:
			n += 2
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			binary.BigEndian.PutUint32(key[prefixLen:], k.Height)
		}
	}

	return key
}

func BlockHashKeyUnpack(key []byte) *BlockHashKey {
	prefixLen := 1
	return &BlockHashKey{
		Prefix: key[:prefixLen],
		Height: binary.BigEndian.Uint32(key[prefixLen:]),
	}
}

func BlockHashValueUnpack(value []byte) *BlockHashValue {
	hash := (*chainhash.Hash)(value)
	return &BlockHashValue{
		BlockHash: hash,
	}
}

type BlockTxsKey struct {
	Prefix []byte `struct:"[1]byte" json:"prefix"`
	Height uint32 `json:"height"`
}

type BlockTxsValue struct {
	TxHashes []*chainhash.Hash `struct-while:"!_eof" json:"tx_hashes"`
}

func (k *BlockTxsKey) NewBlockTxsKey(height uint32) *BlockTxsKey {
	return &BlockTxsKey{
		Prefix: []byte{BlockTXs},
		Height: height,
	}
}

func (k *BlockTxsKey) PackKey() []byte {
	prefixLen := 1
	// b'>L'
	n := prefixLen + 4
	key := make([]byte, n)
	copy(key, k.Prefix)
	binary.BigEndian.PutUint32(key[prefixLen:], k.Height)

	return key
}

func (v *BlockTxsValue) PackValue() []byte {
	numHashes := len(v.TxHashes)
	n := numHashes * 32
	value := make([]byte, n)

	for i, tx := range v.TxHashes {
		if len(tx) != 32 {
			log.Println("Warning, txhash not 32 bytes", tx)
			return nil
		}
		copy(value[i*32:], tx[:])
	}

	return value
}

func (kv *BlockTxsKey) NumFields() int {
	return 1
}

func (k *BlockTxsKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 1 {
		fields = 1
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 4
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			binary.BigEndian.PutUint32(key[prefixLen:], k.Height)
		}
	}

	return key
}

func BlockTxsKeyUnpack(key []byte) *BlockTxsKey {
	prefixLen := 1
	return &BlockTxsKey{
		Prefix: key[:prefixLen],
		Height: binary.BigEndian.Uint32(key[prefixLen:]),
	}
}

func BlockTxsValueUnpack(value []byte) *BlockTxsValue {
	numHashes := len(value) / 32
	txs := make([]*chainhash.Hash, numHashes)
	for i := 0; i < numHashes; i++ {
		txs[i] = (*chainhash.Hash)(value[i*32 : (i+1)*32])
	}
	return &BlockTxsValue{
		TxHashes: txs,
	}
}

type TxCountKey struct {
	Prefix []byte `struct:"[1]byte" json:"prefix"`
	Height uint32 `json:"height"`
}

type TxCountValue struct {
	TxCount uint32 `json:"tx_count"`
}

func NewTxCountKey(height uint32) *TxCountKey {
	return &TxCountKey{
		Prefix: []byte{TxCount},
		Height: height,
	}
}

func (k *TxCountKey) PackKey() []byte {
	prefixLen := 1
	// b'>L'
	n := prefixLen + 4
	key := make([]byte, n)
	copy(key, k.Prefix)
	binary.BigEndian.PutUint32(key[prefixLen:], k.Height)

	return key
}

func (v *TxCountValue) PackValue() []byte {
	value := make([]byte, 4)
	binary.BigEndian.PutUint32(value, v.TxCount)

	return value
}

func (kv *TxCountKey) NumFields() int {
	return 1
}

func (k *TxCountKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 1 {
		fields = 1
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 4
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			binary.BigEndian.PutUint32(key[prefixLen:], k.Height)
		}
	}

	return key
}

func TxCountKeyUnpack(key []byte) *TxCountKey {
	prefixLen := 1
	return &TxCountKey{
		Prefix: key[:prefixLen],
		Height: binary.BigEndian.Uint32(key[prefixLen:]),
	}
}

func TxCountValueUnpack(value []byte) *TxCountValue {
	return &TxCountValue{
		TxCount: binary.BigEndian.Uint32(value),
	}
}

type TxHashKey struct {
	Prefix []byte `struct:"[1]byte" json:"prefix"`
	TxNum  uint32 `json:"tx_num"`
}

type TxHashValue struct {
	TxHash *chainhash.Hash `json:"tx_hash"`
}

func NewTxHashKey(txNum uint32) *TxHashKey {
	return &TxHashKey{
		Prefix: []byte{TxHash},
		TxNum:  txNum,
	}
}

func (k *TxHashKey) PackKey() []byte {
	prefixLen := 1
	// b'>L'
	n := prefixLen + 4
	key := make([]byte, n)
	copy(key, k.Prefix)
	binary.BigEndian.PutUint32(key[prefixLen:], k.TxNum)

	return key
}

func (v *TxHashValue) PackValue() []byte {
	n := len(v.TxHash)
	value := make([]byte, n)
	copy(value, v.TxHash[:n])

	return value
}

func (kv *TxHashKey) NumFields() int {
	return 1
}

func (k *TxHashKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 1 {
		fields = 1
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 4
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			binary.BigEndian.PutUint32(key[prefixLen:], k.TxNum)
		}
	}

	return key
}

func TxHashKeyUnpack(key []byte) *TxHashKey {
	prefixLen := 1
	return &TxHashKey{
		Prefix: key[:prefixLen],
		TxNum:  binary.BigEndian.Uint32(key[prefixLen:]),
	}
}

func TxHashValueUnpack(value []byte) *TxHashValue {
	return &TxHashValue{
		TxHash: (*chainhash.Hash)(value),
	}
}

type TxNumKey struct {
	Prefix []byte          `struct:"[1]byte" json:"prefix"`
	TxHash *chainhash.Hash `json:"tx_hash"`
}

type TxNumValue struct {
	TxNum uint32 `json:"tx_num"`
}

func (k *TxNumKey) PackKey() []byte {
	prefixLen := 1
	// b'>L'
	n := prefixLen + 32
	key := make([]byte, n)
	copy(key, k.Prefix)
	copy(key[prefixLen:], k.TxHash[:32])

	return key
}

func (v *TxNumValue) PackValue() []byte {
	value := make([]byte, 4)
	binary.BigEndian.PutUint32(value, v.TxNum)

	return value
}

func (kv *TxNumKey) NumFields() int {
	return 1
}

func (k *TxNumKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 1 {
		fields = 1
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 32
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			copy(key[prefixLen:], k.TxHash[:32])
		}
	}

	return key
}

func TxNumKeyUnpack(key []byte) *TxNumKey {
	prefixLen := 1
	return &TxNumKey{
		Prefix: key[:prefixLen],
		TxHash: (*chainhash.Hash)(key[prefixLen : prefixLen+32]),
	}
}

func TxNumValueUnpack(value []byte) *TxNumValue {
	return &TxNumValue{
		TxNum: binary.BigEndian.Uint32(value),
	}
}

type TxKey struct {
	Prefix []byte          `struct:"[1]byte" json:"prefix"`
	TxHash *chainhash.Hash `struct:"*[32]byte" json:"tx_hash"`
}

type TxValue struct {
	RawTx []byte `struct-while:"!_eof" json:"raw_tx"`
}

func (k *TxKey) PackKey() []byte {
	prefixLen := 1
	// b'>L'
	n := prefixLen + 32
	key := make([]byte, n)
	copy(key, k.Prefix)
	copy(key[prefixLen:], k.TxHash[:32])

	return key
}

func (v *TxValue) PackValue() []byte {
	value := make([]byte, len(v.RawTx))
	copy(value, v.RawTx)

	return value
}

func (kv *TxKey) NumFields() int {
	return 1
}

func (k *TxKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 1 {
		fields = 1
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 32
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			copy(key[prefixLen:], k.TxHash[:32])
		}
	}

	return key
}

func TxKeyUnpack(key []byte) *TxKey {
	prefixLen := 1
	return &TxKey{
		Prefix: key[:prefixLen],
		TxHash: (*chainhash.Hash)(key[prefixLen : prefixLen+32]),
	}
}

func TxValueUnpack(value []byte) *TxValue {
	return &TxValue{
		RawTx: value,
	}
}

type BlockHeaderKey struct {
	Prefix []byte `struct:"[1]byte" json:"prefix"`
	Height uint32 `json:"height"`
}

type BlockHeaderValue struct {
	Header []byte `struct:"[112]byte" json:"header"`
}

func (k *BlockHeaderValue) Equals(v *BlockHeaderValue) bool {
	return bytes.Equal(k.Header, v.Header)
}

func NewHeaderKey(height uint32) *BlockHeaderKey {
	return &BlockHeaderKey{
		Prefix: []byte{Header},
		Height: height,
	}
}

func (k *BlockHeaderKey) PackKey() []byte {
	prefixLen := 1
	// b'>L'
	n := prefixLen + 4
	key := make([]byte, n)
	copy(key, k.Prefix)
	binary.BigEndian.PutUint32(key[prefixLen:], k.Height)

	return key
}

func (v *BlockHeaderValue) PackValue() []byte {
	value := make([]byte, 112)
	copy(value, v.Header)

	return value
}

func (kv *BlockHeaderKey) NumFields() int {
	return 1
}

func (k *BlockHeaderKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 1 {
		fields = 1
	}
	if fields < 0 {
		fields = 0
	}

	// b'>4sLH'
	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 4
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			binary.BigEndian.PutUint32(key[prefixLen:], k.Height)
		}
	}

	return key
}

func BlockHeaderKeyUnpack(key []byte) *BlockHeaderKey {
	prefixLen := 1
	return &BlockHeaderKey{
		Prefix: key[:prefixLen],
		Height: binary.BigEndian.Uint32(key[prefixLen:]),
	}
}

func BlockHeaderValueUnpack(value []byte) *BlockHeaderValue {
	return &BlockHeaderValue{
		Header: value[:112],
	}
}

type ClaimToTXOKey struct {
	Prefix    []byte `struct:"[1]byte" json:"prefix"`
	ClaimHash []byte `struct:"[20]byte" json:"claim_hash"`
}

type ClaimToTXOValue struct {
	TxNum                   uint32 `json:"tx_num"`
	Position                uint16 `json:"position"`
	RootTxNum               uint32 `json:"root_tx_num"`
	RootPosition            uint16 `json:"root_position"`
	Amount                  uint64 `json:"amount"`
	ChannelSignatureIsValid bool   `json:"channel_signature_is_valid"`
	LengthEncodedName
}

func NewClaimToTXOKey(claimHash []byte) *ClaimToTXOKey {
	return &ClaimToTXOKey{
		Prefix:    []byte{ClaimToTXO},
		ClaimHash: claimHash,
	}
}

func (v *ClaimToTXOValue) NormalizedName() string {
	//TODO implement? Might not need to do anything.
	return internal.NormalizeName(v.Name)
}

func (k *ClaimToTXOKey) PackKey() []byte {
	prefixLen := 1
	// b'>20s'
	n := prefixLen + 20
	key := make([]byte, n)
	copy(key, k.Prefix)
	copy(key[prefixLen:], k.ClaimHash[:20])

	return key
}

func (v *ClaimToTXOValue) PackValue() []byte {
	nameLen := len(v.Name)
	n := 4 + 2 + 4 + 2 + 8 + 1 + 2 + nameLen
	value := make([]byte, n)
	binary.BigEndian.PutUint32(value, v.TxNum)
	binary.BigEndian.PutUint16(value[4:], v.Position)
	binary.BigEndian.PutUint32(value[6:], v.RootTxNum)
	binary.BigEndian.PutUint16(value[10:], v.RootPosition)
	binary.BigEndian.PutUint64(value[12:], v.Amount)
	var bitSetVar uint8
	if v.ChannelSignatureIsValid {
		bitSetVar = 1
	}
	value[20] = bitSetVar
	binary.BigEndian.PutUint16(value[21:], uint16(nameLen))
	copy(value[23:], []byte(v.Name[:nameLen]))

	return value
}

func (kv *ClaimToTXOKey) NumFields() int {
	return 1
}

func (k *ClaimToTXOKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 1 {
		fields = 1
	}
	if fields < 0 {
		fields = 0
	}

	// b'>4sLH'
	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 20
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			copy(key[prefixLen:], k.ClaimHash[:20])
		}
	}

	return key
}

func ClaimToTXOKeyUnpack(key []byte) *ClaimToTXOKey {
	prefixLen := 1
	return &ClaimToTXOKey{
		Prefix:    key[:prefixLen],
		ClaimHash: key[prefixLen : prefixLen+20],
	}
}

func ClaimToTXOValueUnpack(value []byte) *ClaimToTXOValue {
	nameLen := binary.BigEndian.Uint16(value[21:])
	return &ClaimToTXOValue{
		TxNum:                   binary.BigEndian.Uint32(value),
		Position:                binary.BigEndian.Uint16(value[4:]),
		RootTxNum:               binary.BigEndian.Uint32(value[6:]),
		RootPosition:            binary.BigEndian.Uint16(value[10:]),
		Amount:                  binary.BigEndian.Uint64(value[12:]),
		ChannelSignatureIsValid: value[20] == 1,
		LengthEncodedName:       NewLengthEncodedName(string(value[23 : 23+nameLen])),
	}
}

type TXOToClaimKey struct {
	Prefix   []byte `struct:"[1]byte" json:"prefix"`
	TxNum    uint32 `json:"tx_num"`
	Position uint16 `json:"position"`
}

type TXOToClaimValue struct {
	ClaimHash []byte `struct:"[20]byte" json:"claim_hash"`
	LengthEncodedName
}

func NewTXOToClaimKey(txNum uint32, position uint16) *TXOToClaimKey {
	return &TXOToClaimKey{
		Prefix:   []byte{TXOToClaim},
		TxNum:    txNum,
		Position: position,
	}
}

func (k *TXOToClaimKey) PackKey() []byte {
	prefixLen := 1
	// b'>LH'
	n := prefixLen + 4 + 2
	key := make([]byte, n)
	copy(key, k.Prefix)
	binary.BigEndian.PutUint32(key[prefixLen:], k.TxNum)
	binary.BigEndian.PutUint16(key[prefixLen+4:], k.Position)

	return key
}

func (v *TXOToClaimValue) PackValue() []byte {
	nameLen := len(v.Name)
	n := 20 + 2 + nameLen
	value := make([]byte, n)
	copy(value, v.ClaimHash[:20])
	binary.BigEndian.PutUint16(value[20:], uint16(nameLen))
	copy(value[22:], []byte(v.Name))

	return value
}

func (kv *TXOToClaimKey) NumFields() int {
	return 2
}

func (k *TXOToClaimKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 2 {
		fields = 2
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 4
		case 2:
			n += 2
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			binary.BigEndian.PutUint32(key[prefixLen:], k.TxNum)
		case 2:
			binary.BigEndian.PutUint16(key[prefixLen+4:], k.Position)
		}
	}

	return key
}

func TXOToClaimKeyUnpack(key []byte) *TXOToClaimKey {
	prefixLen := 1
	return &TXOToClaimKey{
		Prefix:   key[:prefixLen],
		TxNum:    binary.BigEndian.Uint32(key[prefixLen:]),
		Position: binary.BigEndian.Uint16(key[prefixLen+4:]),
	}
}

func TXOToClaimValueUnpack(value []byte) *TXOToClaimValue {
	nameLen := binary.BigEndian.Uint16(value[20:])
	return &TXOToClaimValue{
		ClaimHash:         value[:20],
		LengthEncodedName: NewLengthEncodedName(string(value[22 : 22+nameLen])),
	}
}

type ClaimShortIDKey struct {
	Prefix                      []byte `struct:"[1]byte" json:"prefix"`
	LengthEncodedNormalizedName        // fields NormalizedNameLen, NormalizedName
	LengthEncodedPartialClaimId        // fields PartialClaimIdLen, PartialClaimId
	RootTxNum                   uint32 `json:"root_tx_num"`
	RootPosition                uint16 `json:"root_position"`
}

type ClaimShortIDValue struct {
	TxNum    uint32 `json:"tx_num"`
	Position uint16 `json:"position"`
}

func NewClaimShortIDKey(normalizedName, partialClaimId string) *ClaimShortIDKey {
	return &ClaimShortIDKey{
		Prefix:                      []byte{ClaimShortIdPrefix},
		LengthEncodedNormalizedName: NewLengthEncodedNormalizedName(normalizedName),
		LengthEncodedPartialClaimId: NewLengthEncodedPartialClaimId(partialClaimId),
	}
}

func (k *ClaimShortIDKey) PackKey() []byte {
	prefixLen := 1
	nameLen := len(k.NormalizedName)
	partialClaimLen := len(k.PartialClaimId)
	log.Printf("nameLen: %d, partialClaimLen: %d\n", nameLen, partialClaimLen)
	n := prefixLen + 2 + nameLen + 1 + partialClaimLen + 4 + 2
	key := make([]byte, n)
	copy(key, k.Prefix)
	binary.BigEndian.PutUint16(key[prefixLen:], uint16(nameLen))
	copy(key[prefixLen+2:], []byte(k.NormalizedName[:nameLen]))
	key[prefixLen+2+nameLen] = uint8(partialClaimLen)
	copy(key[prefixLen+2+nameLen+1:], []byte(k.PartialClaimId[:partialClaimLen]))
	binary.BigEndian.PutUint32(key[prefixLen+2+nameLen+1+partialClaimLen:], k.RootTxNum)
	binary.BigEndian.PutUint16(key[prefixLen+2+nameLen+1+partialClaimLen+4:], k.RootPosition)

	return key
}

func (v *ClaimShortIDValue) PackValue() []byte {
	value := make([]byte, 6)
	binary.BigEndian.PutUint32(value, v.TxNum)
	binary.BigEndian.PutUint16(value[4:], v.Position)

	return value
}

func (kv *ClaimShortIDKey) NumFields() int {
	return 4
}

func (k *ClaimShortIDKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 4 {
		fields = 4
	}
	if fields < 0 {
		fields = 0
	}

	// b'>4sLH'
	prefixLen := 1
	nameLen := len(k.NormalizedName)
	partialClaimLen := len(k.PartialClaimId)

	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 2 + nameLen
		case 2:
			n += 1 + partialClaimLen
		case 3:
			n += 4
		case 4:
			n += 2
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			binary.BigEndian.PutUint16(key[prefixLen:], uint16(nameLen))
			copy(key[prefixLen+2:], []byte(k.NormalizedName))
		case 2:
			key[prefixLen+2+nameLen] = uint8(partialClaimLen)
			copy(key[prefixLen+2+nameLen+1:], []byte(k.PartialClaimId))
		case 3:
			binary.BigEndian.PutUint32(key[prefixLen+2+nameLen+1+partialClaimLen:], k.RootTxNum)
		case 4:
			binary.BigEndian.PutUint16(key[prefixLen+2+nameLen+1+partialClaimLen+4:], k.RootPosition)
		}
	}

	return key
}

func ClaimShortIDKeyUnpack(key []byte) *ClaimShortIDKey {
	prefixLen := 1
	nameLen := int(binary.BigEndian.Uint16(key[prefixLen:]))
	partialClaimLen := int(uint8(key[prefixLen+2+nameLen]))
	return &ClaimShortIDKey{
		Prefix:                      key[:prefixLen],
		LengthEncodedNormalizedName: NewLengthEncodedNormalizedName(string(key[prefixLen+2 : prefixLen+2+nameLen])),
		LengthEncodedPartialClaimId: NewLengthEncodedPartialClaimId(string(key[prefixLen+2+nameLen+1 : prefixLen+2+nameLen+1+partialClaimLen])),
		RootTxNum:                   binary.BigEndian.Uint32(key[prefixLen+2+nameLen+1+partialClaimLen:]),
		RootPosition:                binary.BigEndian.Uint16(key[prefixLen+2+nameLen+1+partialClaimLen+4:]),
	}
}

func ClaimShortIDValueUnpack(value []byte) *ClaimShortIDValue {
	return &ClaimShortIDValue{
		TxNum:    binary.BigEndian.Uint32(value),
		Position: binary.BigEndian.Uint16(value[4:]),
	}
}

type ClaimToChannelKey struct {
	Prefix    []byte `struct:"[1]byte" json:"prefix"`
	ClaimHash []byte `struct:"[20]byte" json:"claim_hash"`
	TxNum     uint32 `json:"tx_num"`
	Position  uint16 `json:"position"`
}

type ClaimToChannelValue struct {
	SigningHash []byte `struct:"[20]byte" json:"signing_hash"`
}

func NewClaimToChannelKey(claimHash []byte, txNum uint32, position uint16) *ClaimToChannelKey {
	return &ClaimToChannelKey{
		Prefix:    []byte{ClaimToChannel},
		ClaimHash: claimHash,
		TxNum:     txNum,
		Position:  position,
	}
}

func (k *ClaimToChannelKey) PackKey() []byte {
	prefixLen := 1
	// b'>20sLH'
	n := prefixLen + 20 + 4 + 2
	key := make([]byte, n)
	copy(key, k.Prefix)
	copy(key[prefixLen:], k.ClaimHash[:20])
	binary.BigEndian.PutUint32(key[prefixLen+20:], k.TxNum)
	binary.BigEndian.PutUint16(key[prefixLen+24:], k.Position)

	return key
}

func (v *ClaimToChannelValue) PackValue() []byte {
	value := make([]byte, 20)
	copy(value, v.SigningHash[:20])

	return value
}

func (kv *ClaimToChannelKey) NumFields() int {
	return 3
}

func (k *ClaimToChannelKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 3 {
		fields = 3
	}
	if fields < 0 {
		fields = 0
	}

	// b'>4sLH'
	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 20
		case 2:
			n += 4
		case 3:
			n += 2
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			copy(key[prefixLen:], k.ClaimHash[:20])
		case 2:
			binary.BigEndian.PutUint32(key[prefixLen+20:], k.TxNum)
		case 3:
			binary.BigEndian.PutUint16(key[prefixLen+24:], k.Position)
		}
	}

	return key
}

func ClaimToChannelKeyUnpack(key []byte) *ClaimToChannelKey {
	prefixLen := 1
	return &ClaimToChannelKey{
		Prefix:    key[:prefixLen],
		ClaimHash: key[prefixLen : prefixLen+20],
		TxNum:     binary.BigEndian.Uint32(key[prefixLen+20:]),
		Position:  binary.BigEndian.Uint16(key[prefixLen+24:]),
	}
}

func ClaimToChannelValueUnpack(value []byte) *ClaimToChannelValue {
	return &ClaimToChannelValue{
		SigningHash: value[:20],
	}
}

type ChannelToClaimKey struct {
	Prefix            []byte `struct:"[1]byte" json:"prefix"`
	SigningHash       []byte `struct:"[20]byte" json:"signing_hash"`
	LengthEncodedName        // fields NameLen, Name
	TxNum             uint32 `json:"tx_num"`
	Position          uint16 `json:"position"`
}

type ChannelToClaimValue struct {
	ClaimHash []byte `struct:"[20]byte" json:"claim_hash"`
}

func NewChannelToClaimKey(channelHash []byte, normalizedName string) *ChannelToClaimKey {
	return &ChannelToClaimKey{
		Prefix:            []byte{ChannelToClaim},
		SigningHash:       channelHash,
		LengthEncodedName: NewLengthEncodedName(normalizedName),
	}
}

func NewChannelToClaimKeyWHash(channelHash []byte) *ChannelToClaimKey {
	return &ChannelToClaimKey{
		Prefix:      []byte{ChannelToClaim},
		SigningHash: channelHash,
	}
}

func (k *ChannelToClaimKey) PackKey() []byte {
	prefixLen := 1
	nameLen := len(k.Name)
	n := prefixLen + 20 + 2 + nameLen + 4 + 2
	key := make([]byte, n)
	copy(key, k.Prefix)
	copy(key[prefixLen:], k.SigningHash[:20])
	binary.BigEndian.PutUint16(key[prefixLen+20:], uint16(nameLen))
	copy(key[prefixLen+22:], []byte(k.Name[:nameLen]))
	binary.BigEndian.PutUint32(key[prefixLen+22+nameLen:], k.TxNum)
	binary.BigEndian.PutUint16(key[prefixLen+22+nameLen+4:], k.Position)

	return key
}

func (v *ChannelToClaimValue) PackValue() []byte {
	value := make([]byte, 20)
	copy(value, v.ClaimHash[:20])

	return value
}

func (kv *ChannelToClaimKey) NumFields() int {
	return 4
}

func (k *ChannelToClaimKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 4 {
		fields = 4
	}
	if fields < 0 {
		fields = 0
	}

	nameLen := len(k.Name)
	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 20
		case 2:
			n += 2 + nameLen
		case 3:
			n += 4
		case 4:
			n += 2
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			copy(key[prefixLen:], k.SigningHash[:20])
		case 2:
			binary.BigEndian.PutUint16(key[prefixLen+20:], uint16(nameLen))
			copy(key[prefixLen+22:], []byte(k.Name))
		case 3:
			binary.BigEndian.PutUint32(key[prefixLen+22+nameLen:], k.TxNum)
		case 4:
			binary.BigEndian.PutUint16(key[prefixLen+22+nameLen+4:], k.Position)
		}
	}

	return key
}

func ChannelToClaimKeyUnpack(key []byte) *ChannelToClaimKey {
	prefixLen := 1
	nameLen := int(binary.BigEndian.Uint16(key[prefixLen+20:]))
	return &ChannelToClaimKey{
		Prefix:            key[:prefixLen],
		SigningHash:       key[prefixLen : prefixLen+20],
		LengthEncodedName: NewLengthEncodedName(string(key[prefixLen+22 : prefixLen+22+nameLen])),
		TxNum:             binary.BigEndian.Uint32(key[prefixLen+22+nameLen:]),
		Position:          binary.BigEndian.Uint16(key[prefixLen+22+nameLen+4:]),
	}
}

func ChannelToClaimValueUnpack(value []byte) *ChannelToClaimValue {
	return &ChannelToClaimValue{
		ClaimHash: value[:20],
	}
}

type ChannelCountKey struct {
	Prefix      []byte `struct:"[1]byte" json:"prefix"`
	ChannelHash []byte `struct:"[20]byte" json:"channel_hash"`
}

type ChannelCountValue struct {
	Count uint32 `json:"count"`
}

func NewChannelCountKey(channelHash []byte) *ChannelCountKey {
	return &ChannelCountKey{
		Prefix:      []byte{ChannelCount},
		ChannelHash: channelHash,
	}
}

func (k *ChannelCountKey) PackKey() []byte {
	prefixLen := 1
	// b'>20sLH'
	n := prefixLen + 20
	key := make([]byte, n)
	copy(key, k.Prefix)
	copy(key[prefixLen:], k.ChannelHash[:20])

	return key
}

func (v *ChannelCountValue) PackValue() []byte {
	value := make([]byte, 4)
	binary.BigEndian.PutUint32(value, v.Count)

	return value
}

func (kv *ChannelCountKey) NumFields() int {
	return 1
}

func (k *ChannelCountKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 1 {
		fields = 1
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 20
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			copy(key[prefixLen:], k.ChannelHash)
		}
	}

	return key
}

func ChannelCountKeyUnpack(key []byte) *ChannelCountKey {
	prefixLen := 1
	return &ChannelCountKey{
		Prefix:      key[:prefixLen],
		ChannelHash: key[prefixLen : prefixLen+20],
	}
}

func ChannelCountValueUnpack(value []byte) *ChannelCountValue {
	return &ChannelCountValue{
		Count: binary.BigEndian.Uint32(value),
	}
}

type SupportAmountKey struct {
	Prefix    []byte `struct:"[1]byte" json:"prefix"`
	ClaimHash []byte `struct:"[20]byte" json:"claim_hash"`
}

type SupportAmountValue struct {
	Amount uint64 `json:"amount"`
}

func NewSupportAmountKey(claimHash []byte) *SupportAmountKey {
	return &SupportAmountKey{
		Prefix:    []byte{SupportAmount},
		ClaimHash: claimHash,
	}
}

func (k *SupportAmountKey) PackKey() []byte {
	prefixLen := 1
	// b'>20sLH'
	n := prefixLen + 20
	key := make([]byte, n)
	copy(key, k.Prefix)
	copy(key[prefixLen:], k.ClaimHash[:20])

	return key
}

func (v *SupportAmountValue) PackValue() []byte {
	value := make([]byte, 8)
	binary.BigEndian.PutUint64(value, v.Amount)

	return value
}

func (kv *SupportAmountKey) NumFields() int {
	return 1
}

func (k *SupportAmountKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 1 {
		fields = 1
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 20
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			copy(key[prefixLen:], k.ClaimHash)
		}
	}

	return key
}

func SupportAmountKeyUnpack(key []byte) *SupportAmountKey {
	prefixLen := 1
	return &SupportAmountKey{
		Prefix:    key[:prefixLen],
		ClaimHash: key[prefixLen : prefixLen+20],
	}
}

func SupportAmountValueUnpack(value []byte) *SupportAmountValue {
	return &SupportAmountValue{
		Amount: binary.BigEndian.Uint64(value),
	}
}

type ClaimToSupportKey struct {
	Prefix    []byte `struct:"[1]byte" json:"prefix"`
	ClaimHash []byte `struct:"[20]byte" json:"claim_hash"`
	TxNum     uint32 `json:"tx_num"`
	Position  uint16 `json:"position"`
}

type ClaimToSupportValue struct {
	Amount uint64 `json:"amount"`
}

func (k *ClaimToSupportKey) PackKey() []byte {
	prefixLen := 1
	// b'>20sLH'
	n := prefixLen + 20 + 4 + 2
	key := make([]byte, n)
	copy(key, k.Prefix)
	copy(key[prefixLen:], k.ClaimHash[:20])
	binary.BigEndian.PutUint32(key[prefixLen+20:], k.TxNum)
	binary.BigEndian.PutUint16(key[prefixLen+24:], k.Position)

	return key
}

func (v *ClaimToSupportValue) PackValue() []byte {
	value := make([]byte, 8)
	binary.BigEndian.PutUint64(value, v.Amount)

	return value
}

func (kv *ClaimToSupportKey) NumFields() int {
	return 3
}

func (k *ClaimToSupportKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 3 {
		fields = 3
	}
	if fields < 0 {
		fields = 0
	}

	// b'>4sLH'
	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 20
		case 2:
			n += 4
		case 3:
			n += 2
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			copy(key[prefixLen:], k.ClaimHash)
		case 2:
			binary.BigEndian.PutUint32(key[prefixLen+20:], k.TxNum)
		case 3:
			binary.BigEndian.PutUint16(key[prefixLen+24:], k.Position)
		}
	}

	return key
}

func ClaimToSupportKeyUnpack(key []byte) *ClaimToSupportKey {
	prefixLen := 1
	return &ClaimToSupportKey{
		Prefix:    key[:prefixLen],
		ClaimHash: key[prefixLen : prefixLen+20],
		TxNum:     binary.BigEndian.Uint32(key[prefixLen+20:]),
		Position:  binary.BigEndian.Uint16(key[prefixLen+24:]),
	}
}

func ClaimToSupportValueUnpack(value []byte) *ClaimToSupportValue {
	return &ClaimToSupportValue{
		Amount: binary.BigEndian.Uint64(value),
	}
}

type SupportToClaimKey struct {
	Prefix   []byte `struct:"[1]byte" json:"prefix"`
	TxNum    uint32 `json:"tx_num"`
	Position uint16 `json:"position"`
}

type SupportToClaimValue struct {
	ClaimHash []byte `struct:"[20]byte" json:"claim_hash"`
}

func (k *SupportToClaimKey) PackKey() []byte {
	prefixLen := 1
	// b'>LH'
	n := prefixLen + 4 + 2
	key := make([]byte, n)
	copy(key, k.Prefix)
	binary.BigEndian.PutUint32(key[prefixLen:], k.TxNum)
	binary.BigEndian.PutUint16(key[prefixLen+4:], k.Position)

	return key
}

func (v *SupportToClaimValue) PackValue() []byte {
	value := make([]byte, 20)
	copy(value, v.ClaimHash)

	return value
}

func (kv *SupportToClaimKey) NumFields() int {
	return 2
}

func (k *SupportToClaimKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 2 {
		fields = 2
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 4
		case 2:
			n += 2
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			binary.BigEndian.PutUint32(key[prefixLen:], k.TxNum)
		case 2:
			binary.BigEndian.PutUint16(key[prefixLen+4:], k.Position)
		}
	}

	return key
}

func SupportToClaimKeyUnpack(key []byte) *SupportToClaimKey {
	prefixLen := 1
	return &SupportToClaimKey{
		Prefix:   key[:prefixLen],
		TxNum:    binary.BigEndian.Uint32(key[prefixLen:]),
		Position: binary.BigEndian.Uint16(key[prefixLen+4:]),
	}
}

func SupportToClaimValueUnpack(value []byte) *SupportToClaimValue {
	return &SupportToClaimValue{
		ClaimHash: value[:20],
	}
}

type ClaimExpirationKey struct {
	Prefix     []byte `struct:"[1]byte" json:"prefix"`
	Expiration uint32 `json:"expiration"`
	TxNum      uint32 `json:"tx_num"`
	Position   uint16 `json:"position"`
}

type ClaimExpirationValue struct {
	ClaimHash                   []byte `struct:"[20]byte" json:"claim_hash"`
	LengthEncodedNormalizedName        // fields NormalizedNameLen, NormalizedName
}

func (k *ClaimExpirationKey) PackKey() []byte {
	prefixLen := 1
	// b'>LLH'
	n := prefixLen + 4 + 4 + 2
	key := make([]byte, n)
	copy(key, k.Prefix)
	binary.BigEndian.PutUint32(key[prefixLen:], k.Expiration)
	binary.BigEndian.PutUint32(key[prefixLen+4:], k.TxNum)
	binary.BigEndian.PutUint16(key[prefixLen+8:], k.Position)

	return key
}

func (v *ClaimExpirationValue) PackValue() []byte {
	nameLen := len(v.NormalizedName)
	n := 20 + 2 + nameLen
	value := make([]byte, n)
	copy(value, v.ClaimHash)
	binary.BigEndian.PutUint16(value[20:], uint16(nameLen))
	copy(value[22:], []byte(v.NormalizedName))

	return value
}

func (kv *ClaimExpirationKey) NumFields() int {
	return 3
}

func (k *ClaimExpirationKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 3 {
		fields = 3
	}
	if fields < 0 {
		fields = 0
	}

	// b'>4sLH'
	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 4
		case 2:
			n += 4
		case 3:
			n += 2
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			binary.BigEndian.PutUint32(key[prefixLen:], k.Expiration)
		case 2:
			binary.BigEndian.PutUint32(key[prefixLen+4:], k.TxNum)
		case 3:
			binary.BigEndian.PutUint16(key[prefixLen+8:], k.Position)
		}
	}

	return key
}

func ClaimExpirationKeyUnpack(key []byte) *ClaimExpirationKey {
	prefixLen := 1
	return &ClaimExpirationKey{
		Prefix:     key[:prefixLen],
		Expiration: binary.BigEndian.Uint32(key[prefixLen:]),
		TxNum:      binary.BigEndian.Uint32(key[prefixLen+4:]),
		Position:   binary.BigEndian.Uint16(key[prefixLen+8:]),
	}
}

func ClaimExpirationValueUnpack(value []byte) *ClaimExpirationValue {
	nameLen := binary.BigEndian.Uint16(value[20:])
	return &ClaimExpirationValue{
		ClaimHash:                   value[:20],
		LengthEncodedNormalizedName: NewLengthEncodedNormalizedName(string(value[22 : 22+nameLen])),
	}
}

type ClaimTakeoverKey struct {
	Prefix                      []byte `struct:"[1]byte" json:"prefix"`
	LengthEncodedNormalizedName        // fields NormalizedNameLen, NormalizedName
}

type ClaimTakeoverValue struct {
	ClaimHash []byte `struct:"[20]byte" json:"claim_hash"`
	Height    uint32 `json:"height"`
}

func NewClaimTakeoverKey(normalizedName string) *ClaimTakeoverKey {
	return &ClaimTakeoverKey{
		Prefix:                      []byte{ClaimTakeover},
		LengthEncodedNormalizedName: NewLengthEncodedNormalizedName(normalizedName),
	}
}

func (v *ClaimTakeoverValue) String() string {
	return fmt.Sprintf(
		"%s(claim_hash=%s, height=%d)",
		reflect.TypeOf(v),
		hex.EncodeToString(v.ClaimHash),
		v.Height,
	)
}

func (k *ClaimTakeoverKey) PackKey() []byte {
	prefixLen := 1
	nameLen := len(k.NormalizedName)
	n := prefixLen + 2 + nameLen
	key := make([]byte, n)
	copy(key, k.Prefix)
	binary.BigEndian.PutUint16(key[prefixLen:], uint16(nameLen))
	copy(key[prefixLen+2:], []byte(k.NormalizedName))

	return key
}

func (v *ClaimTakeoverValue) PackValue() []byte {
	// b'>20sL'
	value := make([]byte, 24)
	copy(value, v.ClaimHash[:20])
	binary.BigEndian.PutUint32(value[20:], uint32(v.Height))

	return value
}

func (kv *ClaimTakeoverKey) NumFields() int {
	return 1
}

func (k *ClaimTakeoverKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 1 {
		fields = 1
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	nameLen := len(k.NormalizedName)
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 2 + nameLen
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			binary.BigEndian.PutUint16(key[prefixLen:], uint16(nameLen))
			copy(key[prefixLen+2:], []byte(k.NormalizedName))
		}
	}

	return key
}

func ClaimTakeoverKeyUnpack(key []byte) *ClaimTakeoverKey {
	prefixLen := 1
	nameLen := binary.BigEndian.Uint16(key[prefixLen:])
	return &ClaimTakeoverKey{
		Prefix:                      key[:prefixLen],
		LengthEncodedNormalizedName: NewLengthEncodedNormalizedName(string(key[prefixLen+2 : prefixLen+2+int(nameLen)])),
	}
}

func ClaimTakeoverValueUnpack(value []byte) *ClaimTakeoverValue {
	return &ClaimTakeoverValue{
		ClaimHash: value[:20],
		Height:    binary.BigEndian.Uint32(value[20:]),
	}
}

type PendingActivationKey struct {
	Prefix   []byte `struct:"[1]byte" json:"prefix"`
	Height   uint32 `json:"height"`
	TxoType  uint8  `json:"txo_type"`
	TxNum    uint32 `json:"tx_num"`
	Position uint16 `json:"position"`
}

func (k *PendingActivationKey) IsSupport() bool {
	return k.TxoType == ActivatedSupportTXOType
}

func (k *PendingActivationKey) IsClaim() bool {
	return k.TxoType == ActivateClaimTXOType
}

type PendingActivationValue struct {
	ClaimHash                   []byte `struct:"[20]byte" json:"claim_hash"`
	LengthEncodedNormalizedName        // fields NormalizedNameLen, NormalizedName
}

func (k *PendingActivationKey) PackKey() []byte {
	prefixLen := 1
	// b'>LBLH'
	n := prefixLen + 4 + 1 + 4 + 2
	key := make([]byte, n)
	copy(key, k.Prefix)
	binary.BigEndian.PutUint32(key[prefixLen:], k.Height)
	key[prefixLen+4] = k.TxoType
	binary.BigEndian.PutUint32(key[prefixLen+5:], k.TxNum)
	binary.BigEndian.PutUint16(key[prefixLen+9:], k.Position)

	return key
}

func (v *PendingActivationValue) PackValue() []byte {
	nameLen := len(v.NormalizedName)
	n := 20 + 2 + nameLen
	value := make([]byte, n)
	copy(value, v.ClaimHash[:20])
	binary.BigEndian.PutUint16(value[20:], uint16(nameLen))
	copy(value[22:], []byte(v.NormalizedName))

	return value
}

func (kv *PendingActivationKey) NumFields() int {
	return 4
}

func (k *PendingActivationKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 4 {
		fields = 4
	}
	if fields < 0 {
		fields = 0
	}

	// b'>4sLH'
	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 4
		case 2:
			n += 1
		case 3:
			n += 4
		case 4:
			n += 2
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			binary.BigEndian.PutUint32(key[prefixLen:], k.Height)
		case 2:
			key[prefixLen+4] = k.TxoType
		case 3:
			binary.BigEndian.PutUint32(key[prefixLen+5:], k.TxNum)
		case 4:
			binary.BigEndian.PutUint16(key[prefixLen+9:], k.Position)
		}
	}

	return key
}

func PendingActivationKeyUnpack(key []byte) *PendingActivationKey {
	prefixLen := 1
	return &PendingActivationKey{
		Prefix:   key[:prefixLen],
		Height:   binary.BigEndian.Uint32(key[prefixLen:]),
		TxoType:  key[prefixLen+4],
		TxNum:    binary.BigEndian.Uint32(key[prefixLen+5:]),
		Position: binary.BigEndian.Uint16(key[prefixLen+9:]),
	}
}

func PendingActivationValueUnpack(value []byte) *PendingActivationValue {
	nameLen := binary.BigEndian.Uint16(value[20:])
	return &PendingActivationValue{
		ClaimHash:                   value[:20],
		LengthEncodedNormalizedName: NewLengthEncodedNormalizedName(string(value[22 : 22+nameLen])),
	}
}

type ActivationKey struct {
	Prefix   []byte `struct:"[1]byte" json:"prefix"`
	TxoType  uint8  `json:"txo_type"`
	TxNum    uint32 `json:"tx_num"`
	Position uint16 `json:"position"`
}

type ActivationValue struct {
	Height                      uint32 `json:"height"`
	ClaimHash                   []byte `struct:"[20]byte" json:"claim_hash"`
	LengthEncodedNormalizedName        // fields NormalizedNameLen, NormalizedName
}

func NewActivationKey(txoType uint8, txNum uint32, position uint16) *ActivationKey {
	return &ActivationKey{
		Prefix:   []byte{ActivatedClaimAndSupport},
		TxoType:  txoType,
		TxNum:    txNum,
		Position: position,
	}
}

func (k *ActivationKey) PackKey() []byte {
	prefixLen := 1
	// b'>BLH'
	n := prefixLen + 1 + 4 + 2
	key := make([]byte, n)
	copy(key, k.Prefix)
	copy(key[prefixLen:], []byte{k.TxoType})
	binary.BigEndian.PutUint32(key[prefixLen+1:], k.TxNum)
	binary.BigEndian.PutUint16(key[prefixLen+5:], k.Position)

	return key
}

func (v *ActivationValue) PackValue() []byte {
	nameLen := len(v.NormalizedName)
	n := 4 + 20 + 2 + nameLen
	value := make([]byte, n)
	binary.BigEndian.PutUint32(value, v.Height)
	copy(value[4:], v.ClaimHash[:20])
	binary.BigEndian.PutUint16(value[24:], uint16(nameLen))
	copy(value[26:], []byte(v.NormalizedName))

	return value
}

func (kv *ActivationKey) NumFields() int {
	return 3
}

func (k *ActivationKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 3 {
		fields = 3
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 1
		case 2:
			n += 4
		case 3:
			n += 2
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			key[prefixLen] = k.TxoType
		case 2:
			binary.BigEndian.PutUint32(key[prefixLen+1:], k.TxNum)
		case 3:
			binary.BigEndian.PutUint16(key[prefixLen+5:], k.Position)
		}
	}

	return key
}

func ActivationKeyUnpack(key []byte) *ActivationKey {
	prefixLen := 1
	return &ActivationKey{
		Prefix:   key[:prefixLen],
		TxoType:  key[prefixLen],
		TxNum:    binary.BigEndian.Uint32(key[prefixLen+1:]),
		Position: binary.BigEndian.Uint16(key[prefixLen+5:]),
	}
}

func ActivationValueUnpack(value []byte) *ActivationValue {
	nameLen := binary.BigEndian.Uint16(value[24:])
	return &ActivationValue{
		Height:                      binary.BigEndian.Uint32(value),
		ClaimHash:                   value[4 : 20+4],
		LengthEncodedNormalizedName: NewLengthEncodedNormalizedName(string(value[26 : 26+nameLen])),
	}
}

type ActiveAmountKey struct {
	Prefix           []byte `struct:"[1]byte" json:"prefix"`
	ClaimHash        []byte `struct:"[20]byte" json:"claim_hash"`
	TxoType          uint8  `json:"txo_type"`
	ActivationHeight uint32 `json:"activation_height"`
	TxNum            uint32 `json:"tx_num"`
	Position         uint16 `json:"position"`
}

type ActiveAmountValue struct {
	Amount uint64 `json:"amount"`
}

func NewActiveAmountKey(claimHash []byte, txoType uint8, activationHeight uint32) *ActiveAmountKey {
	return &ActiveAmountKey{
		Prefix:           []byte{ActiveAmount},
		ClaimHash:        claimHash,
		TxoType:          txoType,
		ActivationHeight: activationHeight,
	}
}

func (k *ActiveAmountKey) PackKey() []byte {
	prefixLen := 1
	// b'>20sBLLH'
	n := prefixLen + 20 + 1 + 4 + 4 + 2
	key := make([]byte, n)
	copy(key, k.Prefix)
	copy(key[prefixLen:], k.ClaimHash[:20])
	copy(key[prefixLen+20:], []byte{k.TxoType})
	binary.BigEndian.PutUint32(key[prefixLen+20+1:], k.ActivationHeight)
	binary.BigEndian.PutUint32(key[prefixLen+20+1+4:], k.TxNum)
	binary.BigEndian.PutUint16(key[prefixLen+20+1+4+4:], k.Position)

	return key
}

func (v *ActiveAmountValue) PackValue() []byte {
	// b'>Q'
	value := make([]byte, 8)
	binary.BigEndian.PutUint64(value, v.Amount)

	return value
}

func (kv *ActiveAmountKey) NumFields() int {
	return 5
}

func (k *ActiveAmountKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 5 {
		fields = 5
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 20
		case 2:
			n += 1
		case 3:
			n += 4
		case 4:
			n += 4
		case 5:
			n += 2
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			copy(key[prefixLen:], k.ClaimHash)
		case 2:
			copy(key[prefixLen+20:], []byte{k.TxoType})
		case 3:
			binary.BigEndian.PutUint32(key[prefixLen+20+1:], k.ActivationHeight)
		case 4:
			binary.BigEndian.PutUint32(key[prefixLen+20+1+4:], k.TxNum)
		case 5:
			binary.BigEndian.PutUint16(key[prefixLen+20+1+4+4:], k.Position)
		}
	}

	return key
}

func ActiveAmountKeyUnpack(key []byte) *ActiveAmountKey {
	prefixLen := 1
	return &ActiveAmountKey{
		Prefix:           key[:prefixLen],
		ClaimHash:        key[prefixLen : prefixLen+20],
		TxoType:          uint8(key[prefixLen+20 : prefixLen+20+1][0]),
		ActivationHeight: binary.BigEndian.Uint32(key[prefixLen+20+1:]),
		TxNum:            binary.BigEndian.Uint32(key[prefixLen+20+1+4:]),
		Position:         binary.BigEndian.Uint16(key[prefixLen+20+1+4+4:]),
	}
}

func ActiveAmountValueUnpack(value []byte) *ActiveAmountValue {
	return &ActiveAmountValue{
		Amount: binary.BigEndian.Uint64(value),
	}
}

type OnesComplementEffectiveAmount uint64

type BidOrderKey struct {
	Prefix                      []byte                        `struct:"[1]byte" json:"prefix"`
	LengthEncodedNormalizedName                               // fields NormalizedNameLen, NormalizedName
	EffectiveAmount             OnesComplementEffectiveAmount `json:"effective_amount"`
	TxNum                       uint32                        `json:"tx_num"`
	Position                    uint16                        `json:"position"`
}

type BidOrderValue struct {
	ClaimHash []byte `struct:"[20]byte" json:"claim_hash"`
}

func NewBidOrderKey(normalizedName string) *BidOrderKey {
	return &BidOrderKey{
		Prefix:                      []byte{BidOrder},
		LengthEncodedNormalizedName: NewLengthEncodedNormalizedName(normalizedName),
	}
}

func (k *BidOrderKey) PackKey() []byte {
	prefixLen := 1
	// 2 byte length field, plus number of bytes in name
	nameLen := len(k.NormalizedName)
	nameLenLen := 2 + nameLen
	// b'>QLH'
	n := prefixLen + nameLenLen + 8 + 4 + 2
	key := make([]byte, n)
	copy(key, k.Prefix)

	binary.BigEndian.PutUint16(key[prefixLen:], uint16(nameLen))
	copy(key[prefixLen+2:], []byte(k.NormalizedName))
	binary.BigEndian.PutUint64(key[prefixLen+nameLenLen:], OnesCompTwiddle64-uint64(k.EffectiveAmount))
	binary.BigEndian.PutUint32(key[prefixLen+nameLenLen+8:], k.TxNum)
	binary.BigEndian.PutUint16(key[prefixLen+nameLenLen+8+4:], k.Position)

	return key
}

func (v *BidOrderValue) PackValue() []byte {
	// b'>20s'
	value := make([]byte, 20)
	copy(value, v.ClaimHash[:20])

	return value
}

func (kv *BidOrderKey) NumFields() int {
	return 4
}

func (k *BidOrderKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	nameLen := len(k.NormalizedName)
	nameLenLen := 2 + nameLen
	if fields > 4 {
		fields = 4
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 2 + nameLen
		case 2:
			n += 8
		case 3:
			n += 4
		case 4:
			n += 2
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			binary.BigEndian.PutUint16(key[prefixLen:], uint16(nameLen))
			copy(key[prefixLen+2:], []byte(k.NormalizedName))
		case 2:
			binary.BigEndian.PutUint64(key[prefixLen+nameLenLen:], OnesCompTwiddle64-uint64(k.EffectiveAmount))
		case 3:
			binary.BigEndian.PutUint32(key[prefixLen+nameLenLen+8:], k.TxNum)
		case 4:
			binary.BigEndian.PutUint16(key[prefixLen+nameLenLen+8+4:], k.Position)
		}
	}

	return key
}

func BidOrderKeyUnpack(key []byte) *BidOrderKey {
	prefixLen := 1
	nameLen := binary.BigEndian.Uint16(key[prefixLen:])
	return &BidOrderKey{
		Prefix:                      key[:prefixLen],
		LengthEncodedNormalizedName: NewLengthEncodedNormalizedName(string(key[prefixLen+2 : prefixLen+2+int(nameLen)])),
		EffectiveAmount:             OnesComplementEffectiveAmount(OnesCompTwiddle64 - binary.BigEndian.Uint64(key[prefixLen+2+int(nameLen):])),
		TxNum:                       binary.BigEndian.Uint32(key[prefixLen+2+int(nameLen)+8:]),
		Position:                    binary.BigEndian.Uint16(key[prefixLen+2+int(nameLen)+8+4:]),
	}
}

func BidOrderValueUnpack(value []byte) *BidOrderValue {
	return &BidOrderValue{
		ClaimHash: value[:20],
	}
}

type RepostKey struct {
	Prefix    []byte `struct:"[1]byte" json:"prefix"`
	ClaimHash []byte `struct:"[20]byte" json:"claim_hash"`
}

type RepostValue struct {
	RepostedClaimHash []byte `struct:"[20]byte" json:"reposted_claim_hash"`
}

func NewRepostKey(claimHash []byte) *RepostKey {
	return &RepostKey{
		Prefix:    []byte{Repost},
		ClaimHash: claimHash,
	}
}

func (k *RepostKey) PackKey() []byte {
	prefixLen := 1
	// b'>20s'
	n := prefixLen + 20
	key := make([]byte, n)
	copy(key, k.Prefix)
	copy(key[prefixLen:], k.ClaimHash)

	return key
}

func (v *RepostValue) PackValue() []byte {
	// FIXME: Is there a limit to this length?
	n := len(v.RepostedClaimHash)
	value := make([]byte, n)
	copy(value, v.RepostedClaimHash)

	return value
}

func (kv *RepostKey) NumFields() int {
	return 1
}

func (k *RepostKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 1 {
		fields = 1
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 20
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			copy(key[prefixLen:], k.ClaimHash)
		}
	}

	return key
}

func RepostKeyUnpack(key []byte) *RepostKey {
	prefixLen := 1
	return &RepostKey{
		Prefix:    key[:prefixLen],
		ClaimHash: key[prefixLen : prefixLen+20],
	}
}

func RepostValueUnpack(value []byte) *RepostValue {
	return &RepostValue{
		RepostedClaimHash: value[:],
	}
}

type RepostedKey struct {
	Prefix            []byte `struct:"[1]byte" json:"prefix"`
	RepostedClaimHash []byte `struct:"[20]byte" json:"reposted_claim_hash"`
	TxNum             uint32 `json:"tx_num"`
	Position          uint16 `json:"position"`
}

type RepostedValue struct {
	ClaimHash []byte `struct:"[20]byte" json:"claim_hash"`
}

func NewRepostedKey(claimHash []byte) *RepostedKey {
	return &RepostedKey{
		Prefix:            []byte{RepostedClaim},
		RepostedClaimHash: claimHash,
	}
}

func (k *RepostedKey) PackKey() []byte {
	prefixLen := 1
	// b'>20sLH'
	n := prefixLen + 20 + 4 + 2
	key := make([]byte, n)
	copy(key, k.Prefix)
	copy(key[prefixLen:], k.RepostedClaimHash)
	binary.BigEndian.PutUint32(key[prefixLen+20:], k.TxNum)
	binary.BigEndian.PutUint16(key[prefixLen+24:], k.Position)

	return key
}

func (v *RepostedValue) PackValue() []byte {
	// b'>20s'
	value := make([]byte, 20)
	copy(value, v.ClaimHash)

	return value
}

func (kv *RepostedKey) NumFields() int {
	return 3
}

func (k *RepostedKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 3 {
		fields = 3
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 20
		case 2:
			n += 4
		case 3:
			n += 2
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			copy(key[prefixLen:], k.RepostedClaimHash)
		case 2:
			binary.BigEndian.PutUint32(key[prefixLen+20:], k.TxNum)
		case 3:
			binary.BigEndian.PutUint16(key[prefixLen+24:], k.Position)
		}
	}

	return key
}

func RepostedKeyUnpack(key []byte) *RepostedKey {
	prefixLen := 1
	return &RepostedKey{
		Prefix:            key[:prefixLen],
		RepostedClaimHash: key[prefixLen : prefixLen+20],
		TxNum:             binary.BigEndian.Uint32(key[prefixLen+20:]),
		Position:          binary.BigEndian.Uint16(key[prefixLen+24:]),
	}
}

func RepostedValueUnpack(value []byte) *RepostedValue {
	return &RepostedValue{
		ClaimHash: value[:20],
	}
}

type RepostedCountKey struct {
	Prefix    []byte `struct:"[1]byte" json:"prefix"`
	ClaimHash []byte `struct:"[20]byte" json:"claim_hash"`
}

type RepostedCountValue struct {
	RepostedCount uint32 `json:"reposted_count"`
}

func (kv *RepostedCountKey) NumFields() int {
	return 1
}

func (kv *RepostedCountKey) PartialPack(fields int) []byte {
	// b'>20s'
	n := len(kv.Prefix) + 20
	buf := make([]byte, n)
	offset := 0
	offset += copy(buf[offset:], kv.Prefix[:1])
	if fields <= 0 {
		return buf[:offset]
	}
	offset += copy(buf[offset:], kv.ClaimHash[:20])
	return buf[:offset]
}

func (kv *RepostedCountKey) PackKey() []byte {
	return kv.PartialPack(kv.NumFields())
}

func (kv *RepostedCountKey) UnpackKey(buf []byte) {
	// b'>20s'
	offset := 0
	kv.Prefix = buf[offset : offset+1]
	offset += 1
	kv.ClaimHash = buf[offset : offset+20]
	offset += 20
}

func (kv *RepostedCountValue) PackValue() []byte {
	// b'>L'
	n := 4
	buf := make([]byte, n)
	offset := 0
	binary.BigEndian.PutUint32(buf[offset:], kv.RepostedCount)
	offset += 4
	return buf[:offset]
}

func (kv *RepostedCountValue) UnpackValue(buf []byte) {
	// b'>L'
	offset := 0
	kv.RepostedCount = binary.BigEndian.Uint32(buf[offset:])
	offset += 4
}

type TouchedOrDeletedClaimKey struct {
	Prefix []byte `struct:"[1]byte" json:"prefix"`
	Height int32  `json:"height"`
}

type TouchedOrDeletedClaimValue struct {
	TouchedClaimsLen uint32   `struct:"sizeof=TouchedClaims"`
	DeletedClaimsLen uint32   `struct:"sizeof=DeletedClaims"`
	TouchedClaims    [][]byte `struct:"[][20]byte" json:"touched_claims"`
	DeletedClaims    [][]byte `struct:"[][20]byte" json:"deleted_claims"`
}

func (v *TouchedOrDeletedClaimValue) String() string {
	touchedSB := strings.Builder{}
	touchedLen := len(v.TouchedClaims)
	for i, claim := range v.TouchedClaims {
		touchedSB.WriteString(hex.EncodeToString(claim))
		if i < touchedLen-1 {
			touchedSB.WriteString(",")
		}
	}

	deletedSB := strings.Builder{}
	deletedLen := len(v.DeletedClaims)
	for i, claim := range v.DeletedClaims {
		deletedSB.WriteString(hex.EncodeToString(claim))
		if i < deletedLen-1 {
			deletedSB.WriteString(",")
		}
	}

	return fmt.Sprintf(
		"%s(touched_claims=%s, deleted_claims=%s)",
		reflect.TypeOf(v),
		touchedSB.String(),
		deletedSB.String(),
	)
}

func (k *TouchedOrDeletedClaimKey) PackKey() []byte {
	prefixLen := 1
	// b'>L'
	n := prefixLen + 4
	key := make([]byte, n)
	copy(key, k.Prefix)
	binary.BigEndian.PutUint32(key[prefixLen:], uint32(k.Height))

	return key
}

func (v *TouchedOrDeletedClaimValue) PackValue() []byte {
	var touchedLen, deletedLen uint32 = 0, 0
	if v.TouchedClaims != nil {
		for _, claim := range v.TouchedClaims {
			if len(claim) != 20 {
				log.Println("TouchedOrDeletedClaimValue: claim not length 20?!?")
				return nil
			}
		}
		touchedLen = uint32(len(v.TouchedClaims))
	}
	if v.DeletedClaims != nil {
		for _, claim := range v.DeletedClaims {
			if len(claim) != 20 {
				log.Println("TouchedOrDeletedClaimValue: claim not length 20?!?")
				return nil
			}
		}
		deletedLen = uint32(len(v.DeletedClaims))
	}
	n := 4 + 4 + 20*touchedLen + 20*deletedLen
	value := make([]byte, n)
	binary.BigEndian.PutUint32(value, touchedLen)
	binary.BigEndian.PutUint32(value[4:], deletedLen)
	// These are sorted for consistency with the Python implementation
	sort.Slice(v.TouchedClaims, func(i, j int) bool { return bytes.Compare(v.TouchedClaims[i], v.TouchedClaims[j]) < 0 })
	sort.Slice(v.DeletedClaims, func(i, j int) bool { return bytes.Compare(v.DeletedClaims[i], v.DeletedClaims[j]) < 0 })

	var i = 8
	for j := 0; j < int(touchedLen); j++ {
		copy(value[i:], v.TouchedClaims[j])
		i += 20
	}
	for j := 0; j < int(deletedLen); j++ {
		copy(value[i:], v.DeletedClaims[j])
		i += 20
	}

	return value
}

func (kv *TouchedOrDeletedClaimKey) NumFields() int {
	return 1
}

func (k *TouchedOrDeletedClaimKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 1 {
		fields = 1
	}
	if fields < 0 {
		fields = 0
	}

	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 4
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			binary.BigEndian.PutUint32(key[prefixLen:], uint32(k.Height))
		}
	}

	return key
}

func TouchedOrDeletedClaimKeyUnpack(key []byte) *TouchedOrDeletedClaimKey {
	prefixLen := 1
	return &TouchedOrDeletedClaimKey{
		Prefix: key[:prefixLen],
		Height: int32(binary.BigEndian.Uint32(key[prefixLen:])),
	}
}

func TouchedOrDeletedClaimValueUnpack(value []byte) *TouchedOrDeletedClaimValue {
	touchedLen := binary.BigEndian.Uint32(value)
	deletedLen := binary.BigEndian.Uint32(value[4:])
	touchedClaims := make([][]byte, touchedLen)
	deletedClaims := make([][]byte, deletedLen)
	var j = 8
	for i := 0; i < int(touchedLen); i++ {
		touchedClaims[i] = value[j : j+20]
		j += 20
	}
	for i := 0; i < int(deletedLen); i++ {
		deletedClaims[i] = value[j : j+20]
		j += 20
	}
	return &TouchedOrDeletedClaimValue{
		TouchedClaimsLen: touchedLen,
		DeletedClaimsLen: deletedLen,
		TouchedClaims:    touchedClaims,
		DeletedClaims:    deletedClaims,
	}
}

func (k *UTXOKey) String() string {
	return fmt.Sprintf(
		"%s(hashX=%s, tx_num=%d, nout=%d)",
		reflect.TypeOf(k),
		hex.EncodeToString(k.HashX),
		k.TxNum,
		k.Nout,
	)
}

func (k *UTXOKey) PackKey() []byte {
	prefixLen := 1
	// b'>11sLH'
	n := prefixLen + 11 + 4 + 2
	key := make([]byte, n)
	copy(key, k.Prefix)
	copy(key[prefixLen:], k.HashX)
	binary.BigEndian.PutUint32(key[prefixLen+11:], k.TxNum)
	binary.BigEndian.PutUint16(key[prefixLen+15:], k.Nout)

	return key
}

func (k *UTXOValue) PackValue() []byte {
	value := make([]byte, 8)
	binary.BigEndian.PutUint64(value, k.Amount)

	return value
}

func (kv *UTXOKey) NumFields() int {
	return 3
}

// UTXOKeyPackPartial packs a variable number of fields for a UTXOKey into
// a byte array.
func (k *UTXOKey) PartialPack(fields int) []byte {
	// Limit fields between 0 and number of fields, we always at least need
	// the prefix, and we never need to iterate past the number of fields.
	if fields > 3 {
		fields = 3
	}
	if fields < 0 {
		fields = 0
	}

	// b'>11sLH'
	prefixLen := 1
	var n = prefixLen
	for i := 0; i <= fields; i++ {
		switch i {
		case 1:
			n += 11
		case 2:
			n += 4
		case 3:
			n += 2
		}
	}

	key := make([]byte, n)

	for i := 0; i <= fields; i++ {
		switch i {
		case 0:
			copy(key, k.Prefix)
		case 1:
			copy(key[prefixLen:], k.HashX)
		case 2:
			binary.BigEndian.PutUint32(key[prefixLen+11:], k.TxNum)
		case 3:
			binary.BigEndian.PutUint16(key[prefixLen+15:], k.Nout)
		}
	}

	return key
}

func UTXOKeyUnpack(key []byte) *UTXOKey {
	prefixLen := 1
	return &UTXOKey{
		Prefix: key[:prefixLen],
		HashX:  key[prefixLen : prefixLen+11],
		TxNum:  binary.BigEndian.Uint32(key[prefixLen+11:]),
		Nout:   binary.BigEndian.Uint16(key[prefixLen+15:]),
	}
}

func UTXOValueUnpack(value []byte) *UTXOValue {
	return &UTXOValue{
		Amount: binary.BigEndian.Uint64(value),
	}
}

type TrendingNotificationKey struct {
	Prefix    []byte `struct:"[1]byte"  json:"prefix"`
	Height    uint32 `json:"height"`
	ClaimHash []byte `struct:"[20]byte" json:"claim_hash"`
}

type TrendingNotificationValue struct {
	PreviousAmount uint64 `json:"previous_amount"`
	NewAmount      uint64 `json:"new_amount"`
}

func (kv *TrendingNotificationKey) NumFields() int {
	return 2
}

func (kv *TrendingNotificationKey) PartialPack(fields int) []byte {
	// b'>L20s'
	n := len(kv.Prefix) + 4 + 20
	buf := make([]byte, n)
	offset := 0
	offset += copy(buf, kv.Prefix[offset:])
	if fields <= 0 {
		return buf[:offset]
	}
	binary.BigEndian.PutUint32(buf[offset:], kv.Height)
	offset += 4
	if fields -= 1; fields <= 0 {
		return buf[:offset]
	}
	offset += copy(buf[offset:], kv.ClaimHash[:20])
	return buf[:offset]
}

func (kv *TrendingNotificationKey) PackKey() []byte {
	return kv.PartialPack(kv.NumFields())
}

func (kv *TrendingNotificationKey) UnpackKey(buf []byte) {
	// b'>L20s'
	offset := 0
	kv.Prefix = buf[offset : offset+1]
	offset += 1
	kv.Height = binary.BigEndian.Uint32(buf[offset:])
	offset += 4
	kv.ClaimHash = buf[offset : offset+20]
	offset += 20
}

func (kv *TrendingNotificationValue) PackValue() []byte {
	// b'>QQ'
	n := 8 + 8
	buf := make([]byte, n)
	offset := 0
	binary.BigEndian.PutUint64(buf[offset:], kv.PreviousAmount)
	offset += 8
	binary.BigEndian.PutUint64(buf[offset:], kv.NewAmount)
	offset += 8
	return buf
}

func (kv *TrendingNotificationValue) UnpackValue(buf []byte) {
	// b'>QQ'
	offset := 0
	kv.PreviousAmount = binary.BigEndian.Uint64(buf[offset:])
	offset += 8
	kv.NewAmount = binary.BigEndian.Uint64(buf[offset:])
	offset += 8
}

type MempoolTxKey struct {
	Prefix []byte `struct:"[1]byte"  json:"prefix"`
	TxHash []byte `struct:"[32]byte" json:"tx_hash"`
}

type MempoolTxValue struct {
	RawTx []byte `struct-while:"!_eof" json:"raw_tx"`
}

func (kv *MempoolTxKey) NumFields() int {
	return 1
}

func (kv *MempoolTxKey) PartialPack(fields int) []byte {
	// b'>32s'
	n := len(kv.Prefix) + 32
	buf := make([]byte, n)
	offset := 0
	offset += copy(buf[offset:], kv.Prefix[:1])
	if fields <= 0 {
		return buf[:offset]
	}
	offset += copy(buf[offset:], kv.TxHash[:32])
	return buf[:offset]
}

func (kv *MempoolTxKey) PackKey() []byte {
	return kv.PartialPack(kv.NumFields())
}

func (kv *MempoolTxKey) UnpackKey(buf []byte) {
	// b'>32s'
	offset := 0
	kv.Prefix = buf[offset : offset+1]
	offset += 1
	kv.TxHash = buf[offset : offset+32]
	offset += 32
}

func (kv *MempoolTxValue) PackValue() []byte {
	// variable length bytes
	n := len(kv.RawTx)
	buf := make([]byte, n)
	offset := 0
	offset += copy(buf, kv.RawTx)
	return buf
}

func (kv *MempoolTxValue) UnpackValue(buf []byte) {
	// variable length bytes
	offset := 0
	kv.RawTx = buf[:]
	offset += len(buf)
}

type TouchedHashXKey struct {
	Prefix []byte `struct:"[1]byte" json:"prefix"`
	Height uint32 `json:"height"`
}

type TouchedHashXValue struct {
	TouchedHashXs [][]byte `struct:"[][11]byte" struct-while:"!_eof" json:"touched_hashXs"`
}

func (kv *TouchedHashXKey) NumFields() int {
	return 1
}

func (kv *TouchedHashXKey) PartialPack(fields int) []byte {
	// b'>L'
	n := len(kv.Prefix) + 4
	buf := make([]byte, n)
	offset := 0
	offset += copy(buf[offset:], kv.Prefix[:1])
	if fields <= 0 {
		return buf[:offset]
	}
	binary.BigEndian.PutUint32(buf[offset:], kv.Height)
	offset += 4
	return buf[:offset]
}

func (kv *TouchedHashXKey) PackKey() []byte {
	return kv.PartialPack(kv.NumFields())
}

func (kv *TouchedHashXKey) UnpackKey(buf []byte) {
	// b'>L'
	offset := 0
	kv.Prefix = buf[offset : offset+1]
	offset += 1
	kv.Height = binary.BigEndian.Uint32(buf[offset:])
	offset += 4
}

func (kv *TouchedHashXValue) PackValue() []byte {
	// variable length bytes
	n := len(kv.TouchedHashXs) * 11
	buf := make([]byte, n)
	offset := 0
	for i := range kv.TouchedHashXs {
		offset += copy(buf[offset:], kv.TouchedHashXs[i][:11])
	}
	return buf
}

func (kv *TouchedHashXValue) UnpackValue(buf []byte) {
	// variable length bytes
	n := len(buf)
	kv.TouchedHashXs = make([][]byte, n/11)
	for i, offset := 0, 0; offset+11 <= n; i, offset = i+1, offset+11 {
		kv.TouchedHashXs[i] = buf[offset : offset+11]
	}
}

type HashXStatusKey struct {
	Prefix []byte `struct:"[1]byte" json:"prefix"`
	HashX  []byte `struct:"[20]byte" json:"hashX"`
}

type HashXStatusValue struct {
	Status []byte `struct:"[32]byte" json:"status"`
}

func (kv *HashXStatusKey) NumFields() int {
	return 1
}

func (kv *HashXStatusKey) PartialPack(fields int) []byte {
	// b'>20s'
	n := len(kv.Prefix) + 20
	buf := make([]byte, n)
	offset := 0
	offset += copy(buf[offset:], kv.Prefix[:1])
	if fields <= 0 {
		return buf[:offset]
	}
	offset += copy(buf[offset:], kv.HashX[:20])
	return buf[:offset]
}

func (kv *HashXStatusKey) PackKey() []byte {
	return kv.PartialPack(kv.NumFields())
}

func (kv *HashXStatusKey) UnpackKey(buf []byte) {
	// b'>20s'
	offset := 0
	kv.Prefix = buf[offset : offset+1]
	offset += 1
	kv.HashX = buf[offset : offset+20]
	offset += 20
}

func (kv *HashXStatusValue) PackValue() []byte {
	// b'32s'
	n := 32
	buf := make([]byte, n)
	offset := 0
	offset += copy(buf[offset:], kv.Status[:32])
	return buf
}

func (kv *HashXStatusValue) UnpackValue(buf []byte) {
	// b'32s'
	offset := 0
	kv.Status = buf[offset : offset+32]
	offset += 32
}

type HashXMempoolStatusKey = HashXStatusKey
type HashXMempoolStatusValue = HashXStatusValue

type EffectiveAmountKey struct {
	Prefix    []byte `struct:"[1]byte" json:"prefix"`
	ClaimHash []byte `struct:"[20]byte" json:"claim_hash"`
}

type EffectiveAmountValue struct {
	ActivatedSum        uint64 `json:"activated_sum"`
	ActivatedSupportSum uint64 `json:"activated_support_sum"`
}

func (kv *EffectiveAmountKey) NumFields() int {
	return 1
}

func (kv *EffectiveAmountKey) PartialPack(fields int) []byte {
	// b'>20s'
	n := len(kv.Prefix) + 20
	buf := make([]byte, n)
	offset := 0
	offset += copy(buf[offset:], kv.Prefix[:1])
	if fields <= 0 {
		return buf[:offset]
	}
	offset += copy(buf[offset:], kv.ClaimHash[:20])
	return buf[:offset]
}

func (kv *EffectiveAmountKey) PackKey() []byte {
	return kv.PartialPack(kv.NumFields())
}

func (kv *EffectiveAmountKey) UnpackKey(buf []byte) {
	// b'>20s'
	offset := 0
	kv.Prefix = buf[offset : offset+1]
	offset += 1
	kv.ClaimHash = buf[offset : offset+20]
	offset += 20
}

func (kv *EffectiveAmountValue) PackValue() []byte {
	// b'>QQ'
	n := 8 + 8
	buf := make([]byte, n)
	offset := 0
	binary.BigEndian.PutUint64(buf[offset:], kv.ActivatedSum)
	offset += 8
	binary.BigEndian.PutUint64(buf[offset:], kv.ActivatedSupportSum)
	offset += 8
	return buf[:offset]
}

func (kv *EffectiveAmountValue) UnpackValue(buf []byte) {
	// b'>QQ'
	offset := 0
	kv.ActivatedSum = binary.BigEndian.Uint64(buf[offset:])
	offset += 8
	kv.ActivatedSupportSum = binary.BigEndian.Uint64(buf[offset:])
	offset += 8
}

func UnpackGenericKey(key []byte) (BaseKey, error) {
	if len(key) == 0 {
		return nil, fmt.Errorf("key length zero")
	}
	// Look up the prefix metadata, and use the registered function(s)
	// to create and unpack key of appropriate type.
	t, ok := prefixRegistry[key[0]]
	if !ok {
		return nil, fmt.Errorf("unpack key function for %v not implemented", key[0])
	}
	if t.newKeyUnpack != nil {
		// Type provides <Type>KeyUnpack() function.
		return t.newKeyUnpack(key).(BaseKey), nil
	}
	if t.newKey != nil {
		// Type provides a new<Type> function.
		k := t.newKey()
		unpacker, ok := k.(KeyUnpacker)
		if ok {
			unpacker.UnpackKey(key)
			return unpacker.(BaseKey), nil
		}
	}
	return nil, fmt.Errorf("unpack key function for %v not implemented", key[0])
}

func UnpackGenericValue(key, value []byte) (BaseValue, error) {
	if len(key) == 0 {
		return nil, fmt.Errorf("key length zero")
	}
	if len(value) == 0 {
		return nil, fmt.Errorf("value length zero")
	}
	// Look up the prefix metadata, and use the registered function(s)
	// to create and unpack value of appropriate type.
	t, ok := prefixRegistry[key[0]]
	if !ok {
		return nil, fmt.Errorf("unpack value function for %v not implemented", key[0])
	}
	if t.newValueUnpack != nil {
		// Type provides <Type>ValueUnpack() function.
		return t.newValueUnpack(value).(BaseValue), nil
	}
	if t.newValue != nil {
		// Type provides a new<Type> function.
		k := t.newValue()
		unpacker, ok := k.(ValueUnpacker)
		if ok {
			unpacker.UnpackValue(value)
			return unpacker.(BaseValue), nil
		}
	}
	return nil, fmt.Errorf("unpack key function for %v not implemented", key[0])
}

func PackPartialGenericKey(key BaseKey, fields int) ([]byte, error) {
	if key == nil {
		return nil, fmt.Errorf("key is nil")
	}
	return key.PartialPack(fields), nil
}

func PackGenericKey(key BaseKey) ([]byte, error) {
	if key == nil {
		return nil, fmt.Errorf("key is nil")
	}
	return key.PackKey(), nil
}

func PackGenericValue(value BaseValue) ([]byte, error) {
	if value == nil {
		return nil, fmt.Errorf("value is nil")
	}
	return value.PackValue(), nil
}

// Metadata associated with each prefix/table. Currently used to
// implement generic unpacking.

type prefixMeta struct {
	newKey         func() interface{}
	newValue       func() interface{}
	newKeyUnpack   func([]byte) interface{}
	newValueUnpack func([]byte) interface{}
	API            *SerializationAPI
}

var prefixRegistry = map[byte]prefixMeta{
	ClaimToSupport: {
		newKey: func() interface{} {
			return &ClaimToSupportKey{Prefix: []byte{ClaimToSupport}}
		},
		newValue: func() interface{} {
			return &ClaimToSupportValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return ClaimToSupportKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return ClaimToSupportValueUnpack(buf)
		},
	},
	SupportToClaim: {
		newKey: func() interface{} {
			return &SupportToClaimKey{Prefix: []byte{SupportToClaim}}
		},
		newValue: func() interface{} {
			return &SupportToClaimValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return SupportToClaimKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return SupportToClaimValueUnpack(buf)
		},
	},

	ClaimToTXO: {
		newKey: func() interface{} {
			return &ClaimToTXOKey{Prefix: []byte{ClaimToTXO}}
		},
		newValue: func() interface{} {
			return &ClaimToTXOValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return ClaimToTXOKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return ClaimToTXOValueUnpack(buf)
		},
	},
	TXOToClaim: {
		newKey: func() interface{} {
			return &TXOToClaimKey{Prefix: []byte{TXOToClaim}}
		},
		newValue: func() interface{} {
			return &TXOToClaimValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return TXOToClaimKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return TXOToClaimValueUnpack(buf)
		},
	},

	ClaimToChannel: {
		newKey: func() interface{} {
			return &ClaimToChannelKey{Prefix: []byte{ClaimToChannel}}
		},
		newValue: func() interface{} {
			return &ClaimToChannelValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return ClaimToChannelKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return ClaimToChannelValueUnpack(buf)
		},
	},
	ChannelToClaim: {
		newKey: func() interface{} {
			return &ChannelToClaimKey{Prefix: []byte{ChannelToClaim}}
		},
		newValue: func() interface{} {
			return &ChannelToClaimValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return ChannelToClaimKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return ChannelToClaimValueUnpack(buf)
		},
	},

	ClaimShortIdPrefix: {
		newKey: func() interface{} {
			return &ClaimShortIDKey{Prefix: []byte{ClaimShortIdPrefix}}
		},
		newValue: func() interface{} {
			return &ClaimShortIDValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return ClaimShortIDKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return ClaimShortIDValueUnpack(buf)
		},
	},
	BidOrder: {
		newKey: func() interface{} {
			return &BidOrderKey{Prefix: []byte{BidOrder}}
		},
		newValue: func() interface{} {
			return &BidOrderValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return BidOrderKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return BidOrderValueUnpack(buf)
		},
	},
	ClaimExpiration: {
		newKey: func() interface{} {
			return &ClaimExpirationKey{Prefix: []byte{ClaimExpiration}}
		},
		newValue: func() interface{} {
			return &ClaimExpirationValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return ClaimExpirationKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return ClaimExpirationValueUnpack(buf)
		},
	},

	ClaimTakeover: {
		newKey: func() interface{} {
			return &ClaimTakeoverKey{Prefix: []byte{ClaimTakeover}}
		},
		newValue: func() interface{} {
			return &ClaimTakeoverValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return ClaimTakeoverKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return ClaimTakeoverValueUnpack(buf)
		},
	},
	PendingActivation: {
		newKey: func() interface{} {
			return &PendingActivationKey{Prefix: []byte{PendingActivation}}
		},
		newValue: func() interface{} {
			return &PendingActivationValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return PendingActivationKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return PendingActivationValueUnpack(buf)
		},
	},
	ActivatedClaimAndSupport: {
		newKey: func() interface{} {
			return &ActivationKey{Prefix: []byte{ActivatedClaimAndSupport}}
		},
		newValue: func() interface{} {
			return &ActivationValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return ActivationKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return ActivationValueUnpack(buf)
		},
	},
	ActiveAmount: {
		newKey: func() interface{} {
			return &ActiveAmountKey{Prefix: []byte{ActiveAmount}}
		},
		newValue: func() interface{} {
			return &ActiveAmountValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return ActiveAmountKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return ActiveAmountValueUnpack(buf)
		},
	},

	Repost: {
		newKey: func() interface{} {
			return &RepostKey{Prefix: []byte{Repost}}
		},
		newValue: func() interface{} {
			return &RepostValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return RepostKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return RepostValueUnpack(buf)
		},
	},
	RepostedClaim: {
		newKey: func() interface{} {
			return &RepostedKey{Prefix: []byte{RepostedClaim}}
		},
		newValue: func() interface{} {
			return &RepostedValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return RepostedKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return RepostedValueUnpack(buf)
		},
	},

	Undo: {
		newKey: func() interface{} {
			return &UndoKey{Prefix: []byte{Undo}}
		},
		newValue: func() interface{} {
			return &UndoValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return UndoKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return UndoValueUnpack(buf)
		},
	},
	ClaimDiff: {
		newKey: func() interface{} {
			return &TouchedOrDeletedClaimKey{Prefix: []byte{ClaimDiff}}
		},
		newValue: func() interface{} {
			return &TouchedOrDeletedClaimValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return TouchedOrDeletedClaimKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return TouchedOrDeletedClaimValueUnpack(buf)
		},
	},

	Tx: {
		newKey: func() interface{} {
			return &TxKey{Prefix: []byte{Tx}}
		},
		newValue: func() interface{} {
			return &TxValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return TxKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return TxValueUnpack(buf)
		},
	},
	BlockHash: {
		newKey: func() interface{} {
			return &BlockHashKey{Prefix: []byte{BlockHash}}
		},
		newValue: func() interface{} {
			return &BlockHashValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return BlockHashKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return BlockHashValueUnpack(buf)
		},
	},
	Header: {
		newKey: func() interface{} {
			return &BlockHeaderKey{Prefix: []byte{Header}}
		},
		newValue: func() interface{} {
			return &BlockHeaderValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return BlockHeaderKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return BlockHeaderValueUnpack(buf)
		},
	},
	TxNum: {
		newKey: func() interface{} {
			return &TxNumKey{Prefix: []byte{TxNum}}
		},
		newValue: func() interface{} {
			return &TxNumValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return TxNumKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return TxNumValueUnpack(buf)
		},
	},
	TxCount: {
		newKey: func() interface{} {
			return &TxCountKey{Prefix: []byte{TxCount}}
		},
		newValue: func() interface{} {
			return &TxCountValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return TxCountKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return TxCountValueUnpack(buf)
		},
	},
	TxHash: {
		newKey: func() interface{} {
			return &TxHashKey{Prefix: []byte{TxHash}}
		},
		newValue: func() interface{} {
			return &TxHashValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return TxHashKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return TxHashValueUnpack(buf)
		},
	},
	UTXO: {
		newKey: func() interface{} {
			return &UTXOKey{Prefix: []byte{UTXO}}
		},
		newValue: func() interface{} {
			return &UTXOValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return UTXOKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return UTXOValueUnpack(buf)
		},
	},
	HashXUTXO: {
		newKey: func() interface{} {
			return &HashXUTXOKey{Prefix: []byte{HashXUTXO}}
		},
		newValue: func() interface{} {
			return &HashXUTXOValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return HashXUTXOKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return HashXUTXOValueUnpack(buf)
		},
	},
	HashXHistory: {
		newKey: func() interface{} {
			return &HashXHistoryKey{Prefix: []byte{HashXHistory}}
		},
		newValue: func() interface{} {
			return &HashXHistoryValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return HashXHistoryKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return HashXHistoryValueUnpack(buf)
		},
	},
	DBState: {
		newKey: func() interface{} {
			return &DBStateKey{Prefix: []byte{DBState}}
		},
		newValue: func() interface{} {
			return &DBStateValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return DBStateKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return DBStateValueUnpack(buf)
		},
	},
	ChannelCount: {
		newKey: func() interface{} {
			return &ChannelCountKey{Prefix: []byte{ChannelCount}}
		},
		newValue: func() interface{} {
			return &ChannelCountValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return ChannelCountKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return ChannelCountValueUnpack(buf)
		},
	},
	SupportAmount: {
		newKey: func() interface{} {
			return &SupportAmountKey{Prefix: []byte{SupportAmount}}
		},
		newValue: func() interface{} {
			return &SupportAmountValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return SupportAmountKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return SupportAmountValueUnpack(buf)
		},
	},
	BlockTXs: {
		newKey: func() interface{} {
			return &BlockTxsKey{Prefix: []byte{BlockTXs}}
		},
		newValue: func() interface{} {
			return &BlockTxsValue{}
		},
		newKeyUnpack: func(buf []byte) interface{} {
			return BlockTxsKeyUnpack(buf)
		},
		newValueUnpack: func(buf []byte) interface{} {
			return BlockTxsValueUnpack(buf)
		},
	},

	TrendingNotifications: {
		newKey: func() interface{} {
			return &TrendingNotificationKey{Prefix: []byte{TrendingNotifications}}
		},
		newValue: func() interface{} {
			return &TrendingNotificationValue{}
		},
	},
	MempoolTx: {
		newKey: func() interface{} {
			return &MempoolTxKey{Prefix: []byte{MempoolTx}}
		},
		newValue: func() interface{} {
			return &MempoolTxValue{}
		},
	},
	TouchedHashX: {
		newKey: func() interface{} {
			return &TouchedHashXKey{Prefix: []byte{TouchedHashX}}
		},
		newValue: func() interface{} {
			return &TouchedHashXValue{}
		},
	},
	HashXStatus: {
		newKey: func() interface{} {
			return &HashXStatusKey{Prefix: []byte{HashXStatus}}
		},
		newValue: func() interface{} {
			return &HashXStatusValue{}
		},
	},
	HashXMempoolStatus: {
		newKey: func() interface{} {
			return &HashXMempoolStatusKey{Prefix: []byte{HashXMempoolStatus}}
		},
		newValue: func() interface{} {
			return &HashXMempoolStatusValue{}
		},
	},
	RepostedCount: {
		newKey: func() interface{} {
			return &RepostedCountKey{Prefix: []byte{RepostedCount}}
		},
		newValue: func() interface{} {
			return &RepostedCountValue{}
		},
	},
	EffectiveAmount: {
		newKey: func() interface{} {
			return &EffectiveAmountKey{Prefix: []byte{EffectiveAmount}}
		},
		newValue: func() interface{} {
			return &EffectiveAmountValue{}
		},
	},
}