github.com/onflow/flow-go@v0.35.7-crescendo-preview.23-atree-inlining/ledger/complete/wal/encoding.go (about)

     1  package wal
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/onflow/flow-go/ledger"
     7  	"github.com/onflow/flow-go/ledger/common/utils"
     8  )
     9  
    10  type WALOperation uint8
    11  
    12  const WALUpdate WALOperation = 1
    13  const WALDelete WALOperation = 2
    14  
    15  /*
    16  The LedgerWAL update record uses two operations so far - an update which must include all keys and values, and deletion
    17  which only needs a root tree state commitment.
    18  Updates need to be atomic, hence we prepare binary representation of whole changeset.
    19  Since keys, values and state commitments date types are variable length, we have to store it as well.
    20  If OP = WALDelete, record has:
    21  
    22  1 byte Operation Type | 2 bytes Big Endian uint16 length of state commitment | state commitment data
    23  
    24  If OP = WALUpdate, record has:
    25  
    26  1 byte Operation Type | 2 bytes version | 1 byte TypeTrieUpdate | 2 bytes Big Endian uint16 length of state commitment | state commitment data |
    27  4 bytes Big Endian uint32 - total number of key/value pairs | 2 bytes Big Endian uint16 - length of key (keys are the same length)
    28  
    29  and for every pair after
    30  bytes for key | 4 bytes Big Endian uint32 - length of value | value bytes
    31  
    32  The code here is deliberately simple, for performance.
    33  
    34  */
    35  
    36  func EncodeUpdate(update *ledger.TrieUpdate) []byte {
    37  	encUpdate := ledger.EncodeTrieUpdate(update)
    38  	buf := make([]byte, len(encUpdate)+1)
    39  	// set WAL type
    40  	buf[0] = byte(WALUpdate)
    41  	// TODO use 2 bytes for encoding length
    42  	// the rest is encoded update
    43  	copy(buf[1:], encUpdate)
    44  	return buf
    45  }
    46  
    47  func EncodeDelete(rootHash ledger.RootHash) []byte {
    48  	buf := make([]byte, 0, 1+2+len(rootHash))
    49  	buf = append(buf, byte(WALDelete))
    50  	buf = utils.AppendShortData(buf, rootHash[:])
    51  	return buf
    52  }
    53  
    54  func Decode(data []byte) (operation WALOperation, rootHash ledger.RootHash, update *ledger.TrieUpdate, err error) {
    55  	if len(data) < 4 { // 1 byte op + 2 size + actual data = 4 minimum
    56  		err = fmt.Errorf("data corrupted, too short to represent operation - hexencoded data: %x", data)
    57  		return
    58  	}
    59  
    60  	operation = WALOperation(data[0])
    61  	switch operation {
    62  	case WALUpdate:
    63  		update, err = ledger.DecodeTrieUpdate(data[1:])
    64  		return
    65  	case WALDelete:
    66  		var rootHashBytes []byte
    67  		rootHashBytes, _, err = utils.ReadShortData(data[1:])
    68  		if err != nil {
    69  			err = fmt.Errorf("cannot read state commitment: %w", err)
    70  			return
    71  		}
    72  		rootHash, err = ledger.ToRootHash(rootHashBytes)
    73  		if err != nil {
    74  			err = fmt.Errorf("invalid root hash: %w", err)
    75  			return
    76  		}
    77  		return
    78  	default:
    79  		err = fmt.Errorf("unknown operation type, given: %x", operation)
    80  		return
    81  	}
    82  }