Documentation
¶
Index ¶
- Constants
- Variables
- func BlobRead(ctx context.Context, blobPath string, store massifReader, ...) (*azblob.ReaderResponse, []byte, error)
- func DecodeMassifStart(ms *MassifStart, start []byte) error
- func EmptyIndexEntry() []byte
- func EncodeMassifStart(version uint16, epoch uint32, massifHeight uint8, massifIndex uint32) []byte
- func GetIndexSnowflakeID(data []byte, offset uint64) uint64
- func IndexFromBlobSize(size int) uint64
- func IndexedLogValue(logData []byte, i uint64) []byte
- func MassifFirstLeaf(massifHeight uint8, massifIndex uint32) uint64
- func MassifIndexFromLeafIndex(massifHeight uint8, leafIndex uint64) uint64
- func MassifIndexFromMMRIndex(massifHeight uint8, mmrIndex uint64) (uint64, error)
- func NewIndexEntry(assetId uuid.UUID, eventId uuid.UUID, snowflakeId uint64) []byte
- func RangeLastLeafIndex(firstIndex uint64, height uint8) uint64
- func RangeRootIndex(firstIndex uint64, height uint8) uint64
- func SetIndexEntry(data []byte, offset uint64, assetId uuid.UUID, eventId uuid.UUID, ...)
- func SetIndexSnowflakeID(data []byte, offset uint64, snowflakeId uint64)
- func TenantMassifBlobPath(tenantIdentity string, number uint64) string
- func TenantMassifPrefix(tenantIdentity string) string
- func TreeCount(height uint8) uint64
- func TreeLastLeafIndex(height uint8) uint64
- func TreeRootIndex(height uint8) uint64
- func TreeSize(height uint8) uint64
- type IndexHeader
- type KeyType
- type LogEntry
- type MassifContext
- func (mc MassifContext) Count() uint64
- func (mc MassifContext) FixedHeaderEnd() uint64
- func (mc *MassifContext) Get(i uint64) ([]byte, error)
- func (mc MassifContext) IndexEnd() uint64
- func (mc MassifContext) IndexHeaderEnd() uint64
- func (mc MassifContext) IndexHeaderStart() uint64
- func (mc MassifContext) IndexLen() uint64
- func (mc MassifContext) IndexSize() uint64
- func (mc MassifContext) IndexStart() uint64
- func (mc MassifContext) LogStart() uint64
- func (mc MassifContext) PeakStackStart() uint64
- func (mc MassifContext) RangeCount() uint64
- type MassifReader
- type MassifStart
Constants ¶
const ( IndexEntryBytes = 32 * 2 KeyBitSizeLogBase2 = 8 KeyByteSizeLogBase2 = 5 EventIDFirst = 0 EventIDEnd = EventIDFirst + 16 SnowflakeIdFirst = 24 SnowflakeIdEnd = SnowflakeIdFirst + 8 AssetIDFirst = SnowflakeIdEnd AssetIDEnd = AssetIDFirst + 16 )
const ( ValueBits = 128 ValueBytes = 32 IndexHeaderBytes = 32 LogEntryBytes = 32 EntryByteSizeLogBase2 = 5 ValueBitSizeLogBase2 = 8 ValueByteSizeLogBase2 = 5 )
const ( MassifStartKeyVersionFirstByte = 21 MassifStartKeyVersionSize = 2 // 16 bit MassifStartKeyVersionEnd = MassifStartKeyVersionFirstByte + MassifStartKeyVersionSize MassifStartKeyEpochFirstByte = MassifStartKeyVersionEnd MassifStartKeyEpochSize = 4 // 32 bit MassifStartKeyEpochEnd = MassifStartKeyEpochFirstByte + MassifStartKeyEpochSize // Note the massif height is purposefully ahead of the index, it can't be // changed without also incrementing the EPOCH, so we never care about it's // effect on the sort order with respect to the first index MassifStartKeyMassifHeightFirstByte = MassifStartKeyEpochEnd MassifStartKeyMassifHeightSize = 1 // 8 bit MassifStartKeyMassifHeightEnd = MassifStartKeyMassifHeightFirstByte + MassifStartKeyMassifHeightSize MassifStartKeyMassifFirstByte = MassifStartKeyMassifHeightEnd MassifStartKeyMassifSize = 4 MassifStartKeyMassifEnd = MassifStartKeyMassifFirstByte + MassifStartKeyMassifSize // 32 bit MassifStartKeyFirstIndexFirstByte = MassifStartKeyMassifEnd MassifCurrentVersion = uint16(0) )
const ( V1MMRPrefix = "v1/mmrs" V1MMRBlobNameFmt = "%016d.log" )
Variables ¶
var ( ErrLogEntryToSmall = errors.New("to few bytes to represent a valid log entry") ErrLogValueToSmall = errors.New("to few bytes to represent a valid log value") ErrLogValueBadSize = errors.New("log value size invalid") )
var ( ErrAncestorStackInvalid = errors.New("the ancestor stack is invalid due to bad header information") )
var ( ErrMassifFixedHeaderMissing = errors.New("the fixed header for the massif is missing") ErrMassifFixedHeaderBadType = errors.New("the fixed header for the massif has the wrong type code") ErrEntryTypeUnexpected = errors.New("the entry type was not as expected") ErrEntryTypeInvalid = errors.New("the entry type was invalid") ErrMassifBelowMinSize = errors.New("a massive blob always has at least three log entries") ErrPrevRootNotSet = errors.New("the previous root was not provided") )
var (
ErrIndexEntryBadSize = errors.New("log index size invalid")
)
var (
ErrNotleaf = errors.New("mmr node not a leaf")
)
Functions ¶
func BlobRead ¶
func BlobRead( ctx context.Context, blobPath string, store massifReader, opts ...azblob.Option) (*azblob.ReaderResponse, []byte, error)
BlobRead reads the blob of the given store.
func DecodeMassifStart ¶
func DecodeMassifStart(ms *MassifStart, start []byte) error
func EmptyIndexEntry ¶
func EmptyIndexEntry() []byte
EmptyIndexEntry is a convenience method for unit tests that don't require a valid index entry
func EncodeMassifStart ¶
EncodeMassifStart encodes the massif details in the prescribed massif header record format
. | type| <reserved>| version| epoch |massif height| massif i | . | 0 | | 21 - 22 | 23 26|27 27| 28 - 31 | bytes | 1 | | 2 | 4 | 1 | 4 |
func GetIndexSnowflakeID ¶
func IndexFromBlobSize ¶
func IndexedLogValue ¶
IndexedValue returns the value bytes from log data corresponding to entry index i. No range checks are performed, out of range will panic
func MassifFirstLeaf ¶
MassifFirstLeaf returns the MMR index of the first leaf in the massif blob identified by massifIndex
func MassifIndexFromLeafIndex ¶
MassifIndexFromLeafIndex gets the massif index of the massif that the given leaf is stored in,
given the leaf index of the leaf.
This is found with the given massif height, which is constant for all massifs.
func MassifIndexFromMMRIndex ¶
MassifIndexFromMMRIndex gets the massif index of the massif that the given leaf is stored in
given the mmr index of the leaf.
NOTE: if the mmrIndex is not a leaf node, then error is returned.
func NewIndexEntry ¶
NewIndexEntry creates an index entry directly from the required components
func RangeLastLeafIndex ¶
RangeLastLeafIndex returns the mmr index of the last leaf given the first index of a massif and its height.
func RangeRootIndex ¶
RangeRootIndex return the Massif root node's index in the overall MMR given the massif height and the first index of the MMR it contains
func SetIndexEntry ¶
func SetIndexEntry( data []byte, offset uint64, assetId uuid.UUID, eventId uuid.UUID, snowflakeId uint64, )
SetIndexEntry populates the mmr blob index entry at the provided data offset
| 0 - 127 | 128 - 185| 184 - 191 | 192 - 255 | | event uuid| reserved | reserved (epoch) | snowflakeid| | 0 - 15 | 16 - 22| 23 | 24 - 31| | 16 | 7 | 1 | 8 | | asset uuid| reserved | | 256 - 384| 384 - - 512 | | 16 | 16 |
func SetIndexSnowflakeID ¶
func TenantMassifBlobPath ¶
TenantEpochMountainBlobPath returns the appropriate blob path for the blob
We partition the blob space conveniently for working with the double batched merkle log accumulator scheme described by Justin Drake [here](https://ethresear.ch/t/double-batched-merkle-log-accumulator/571)
The returned string forms a relative resource name with a versioned resource prefix of 'v1/mmrs'
Because azure blob names and tags sort and compare only *lexically*, The number is represented in that path as a 16 digit hex string.
func TenantMassifPrefix ¶
func TreeLastLeafIndex ¶
TreeLastLeafIndex returns the index of the last leaf in the tree with the given height (1 << h) - h -1 works because the number of nodes required to include the last leaf is always equal to the MMR height produced by that
func TreeRootIndex ¶
TreeRootIndex returns the root index for the tree with height
Types ¶
type IndexHeader ¶
type IndexHeader struct {
Index uint64
}
IndexHeader exists to keep track of the number of leaves represented by the mmr data.
Background:
By keeping the index and the log together, we guarantee mutual consistency - provided the log and the idex values are correctly calculated, a single write commits the change back to the blob store.
Because the data is combined, we can't use file size as a proxy for the membership count.
Regardless of whether we pre-allocate the index data or whether we accumulate it as we do the mmr, we need to know how many leaves are in the index. An algorithm to derive a leaf index form an MMR position exists, it is sub linear but a bit fiddly to get right.
At least for now, we are going to explicitly track the count of leaves in a counter value in the blob.
type KeyType ¶
type KeyType uint8
const ( KeyTypeApplicationContent KeyType = iota // this is the standard entry type, purposefuly defined as 0 KeyTypeApplicationLast // first 8 types are reserved for the application // KeyTypeInteriorNode trie keys for MMR interior nodes have this type KeyTypeInteriorNode // KeyTypeMassifStart is the type for keys which correspond to massif blob // header values KeyTypeMassifStart KeyTypeMax )
type MassifContext ¶
type MassifContext struct { TenantIdentity string BlobPath string Tags map[string]string ETag string LastRead time.Time LastModfified time.Time // Read from the first log entry in the blob. If Creating is true and Found // > 0, this is the Start header of the *previous* massif Start MassifStart Data []byte // contains filtered or unexported fields }
1 << 3 - 1 << 2 = 8 - 4 = 4 1 << 4 - 1 << 3 = 16 - 8 = 8
Massif Root Index 7-1 | 8+7-2 | 16 + 7-2 Massif Last Leaf Index 5-1 | 8+5-2 | 16 + 5-2
In order to require the power 2 property for the leaf count, we configure the massif size by its 'height'. Here, our 4 leaf tree has height 3 (level index 2)
So typically instead of n + n -1, where n is the massif leaf count we instead do
Massif Root Index = (1 << h) - 2 Massif Last Leaf Index = (1 << h) - h - 1
func (MassifContext) Count ¶
func (mc MassifContext) Count() uint64
Count returns the number of log entries in the massif
func (MassifContext) FixedHeaderEnd ¶
func (mc MassifContext) FixedHeaderEnd() uint64
func (*MassifContext) Get ¶
func (mc *MassifContext) Get(i uint64) ([]byte, error)
Get returns the value associated with the node at MMR index i
Note that due to the structure of the MMR we are guaranteed that adding a node will only reference other nodes in the *current* massif, OR it will reference the root of the previous massif. As we link the massif blobs by including the root of the previous massif as the value for the first massif entry, we can return it directly. Eg in fhe following, the left child of position 15 is the root of massif 0 at position 7, and similarly, the left child of the root of massif 2 will be position 15. As Get works in indices, that will be indices 14 and 6.
3 \ 15 massif 1 \ . massif 2 \/ \ \ massif 0 /\ \ | / \ \ | 2 ..... 7.....|....14........|...... 22 ..... / \ | / \ | / 1 3 6 | 10 13 | 18 21 / \ / \ | / \ / \ | / \ 1 2 4 5| 8 9 11 12| 16 17 19 20 0 1 3 4| 7 8 10 11| 15 16 18 19 | massif 0 | massif 1 . | massif 2 ....>
This method satisfies the Get method of the MMR NodeAdder interface
func (MassifContext) IndexEnd ¶
func (mc MassifContext) IndexEnd() uint64
IndexEnd returns the byte index of the end of index data
func (MassifContext) IndexHeaderEnd ¶
func (mc MassifContext) IndexHeaderEnd() uint64
IndexHeaderEhd returns the end of the bytes reserved for the index header. Currently, nothing is stored in this. XXX: TODO: Consider removing the field all together
func (MassifContext) IndexHeaderStart ¶
func (mc MassifContext) IndexHeaderStart() uint64
func (MassifContext) IndexLen ¶
func (mc MassifContext) IndexLen() uint64
func (MassifContext) IndexSize ¶
func (mc MassifContext) IndexSize() uint64
func (MassifContext) IndexStart ¶
func (mc MassifContext) IndexStart() uint64
IndexStart returns the index of the first **byte** of index data.
func (MassifContext) LogStart ¶
func (mc MassifContext) LogStart() uint64
func (MassifContext) PeakStackStart ¶
func (mc MassifContext) PeakStackStart() uint64
func (MassifContext) RangeCount ¶
func (mc MassifContext) RangeCount() uint64
RangeCount returns the total number of log entries in the MMR upto and including this context
type MassifReader ¶
type MassifReader struct {
// contains filtered or unexported fields
}
func NewMassifReader ¶
func NewMassifReader(log logger.Logger, store massifReader) *MassifReader
type MassifStart ¶
type MassifStart struct { MassifHeight uint8 Version uint16 Epoch uint32 MassifIndex uint32 FirstIndex uint64 PeakStackLen uint64 }
func NewMassifStart ¶
func NewMassifStart(epoch uint32, massifHeight uint8, massifIndex uint32, firstIndex uint64) MassifStart
func (MassifStart) MarshalBinary ¶
func (ms MassifStart) MarshalBinary() ([]byte, error)
func (*MassifStart) UnmarshalBinary ¶
func (ms *MassifStart) UnmarshalBinary(b []byte) error