API 레퍼런스

← Merkle Trie


MerkleTrieNode

Merkle Trie의 노드를 나타내는 봉인된 trait.

변형 (Variants)

Leaf

final case class Leaf(prefix: Nibbles, value: ByteVector) extends MerkleTrieNode

키-값 쌍을 저장하는 말단 노드.

필드:

예제:

import org.sigilaris.core.merkle.*
import org.sigilaris.core.merkle.Nibbles.{given, *}
import scodec.bits.ByteVector

val leaf = MerkleTrieNode.leaf(
  prefix = ByteVector(0x12, 0x34).toNibbles,
  value = ByteVector.fromValidHex("abcd")
)

Branch

final case class Branch(prefix: Nibbles, children: Children) extends MerkleTrieNode

자식 노드만 가진 분기 노드 (이 노드에 값 없음).

필드:

예제:

import org.sigilaris.core.merkle.*
import org.sigilaris.core.merkle.Nibbles.{given, *}
import org.sigilaris.core.merkle.MerkleTrieNode.*
import scodec.bits.ByteVector

val branch = MerkleTrieNode.branch(
  prefix = ByteVector.empty.toNibbles,
  children = Children.empty
)

BranchWithData

final case class BranchWithData(
  prefix: Nibbles,
  children: Children,
  value: ByteVector
) extends MerkleTrieNode

자식 노드와 값을 모두 가진 분기 노드.

필드:

예제:

import org.sigilaris.core.merkle.*
import org.sigilaris.core.merkle.Nibbles.{given, *}
import org.sigilaris.core.merkle.MerkleTrieNode.*
import scodec.bits.ByteVector
import scodec.bits.ByteVector

val branchWithData = MerkleTrieNode.branchWithData(
  prefix = ByteVector(0x12).toNibbles,
  children = Children.empty,
  value = ByteVector.fromValidHex("data")
)

공통 메서드

모든 MerkleTrieNode 변형은 다음을 지원:

prefix

def prefix: Nibbles

이 노드의 접두사 니블을 반환.

getChildren

def getChildren: Option[Children]

이 노드가 자식을 가지면 자식 배열을 반환.

반환:

getValue

def getValue: Option[ByteVector]

이 노드에 저장된 값을 반환 (있는 경우).

반환:

setPrefix

def setPrefix(prefix: Nibbles): MerkleTrieNode

업데이트된 접두사를 가진 새 노드를 반환.

매개변수:

반환: 업데이트된 접두사를 가진 새 노드

setChildren

def setChildren(children: Children): MerkleTrieNode

업데이트된 자식을 가진 새 노드를 반환.

매개변수:

반환: 업데이트된 자식을 가진 새 노드

setValue

def setValue(value: ByteVector): MerkleTrieNode

업데이트된 값을 가진 새 노드를 반환.

매개변수:

반환: 업데이트된 값을 가진 새 노드

타입 별칭

type MerkleHash = Hash.Value[MerkleTrieNode]
type MerkleRoot = MerkleHash
type Children = Vector[Option[MerkleHash]] :| Length[StrictEqual[16]]

MerkleHash: Merkle Trie 노드의 해시 값 (32바이트 Keccak-256)

MerkleRoot: Merkle Trie의 루트 해시 (MerkleHash의 별칭)

Children: 정확히 16개의 선택적 자식 노드 해시 배열

Children 연산

extension (c: Children)
  def updateChild(i: Int, v: Option[MerkleHash]): Children

주어진 인덱스(0-15)의 자식을 업데이트.

매개변수:

반환: 업데이트된 자식 배열

예제:

import org.sigilaris.core.merkle.MerkleTrieNode.*
import scodec.bits.ByteVector
import org.sigilaris.core.crypto.Hash

val children = Children.empty
val hash: MerkleHash = ??? // 어떤 해시 값
val updated = children.updateChild(5, Some(hash))

Nibbles

4비트 경계에 정렬된 비트 벡터를 나타내는 불투명 타입.

생성

object Nibbles {
  val empty: Nibbles
  def combine(nibbles: Nibbles*): Nibbles
}

empty: 빈 Nibbles 값

combine: 여러 Nibbles를 하나로 결합

예제:

import org.sigilaris.core.merkle.Nibbles
import org.sigilaris.core.merkle.Nibbles.{given, *}
import scodec.bits.{BitVector, ByteVector}

val nibbles1 = ByteVector(0x12).toNibbles
val nibbles2 = ByteVector(0x34).toNibbles
val combined = Nibbles.combine(nibbles1, nibbles2)
// combined는 "1234"를 나타냄

확장 메서드

value

def value: BitVector

기본 BitVector 값을 반환.

bytes

def bytes: ByteVector

ByteVector로 변환.

nibbleSize

def nibbleSize: Long

니블의 개수 (비트 크기 / 4)를 반환.

unCons

def unCons: Option[(Int, Nibbles)]

첫 번째 니블과 나머지를 분리.

반환:

예제:

import org.sigilaris.core.merkle.Nibbles.{given, *}
import scodec.bits.ByteVector

val nibbles = ByteVector(0x1A, 0xBC).toNibbles
val result = nibbles.unCons
// first = 1, rest는 "ABC"를 나타냄

stripPrefix

def stripPrefix(prefix: Nibbles): Option[Nibbles]

주어진 접두사를 제거 (존재하는 경우).

매개변수:

반환:

예제:

import org.sigilaris.core.merkle.Nibbles.{given, *}
import scodec.bits.ByteVector

val nibbles = ByteVector(0x12, 0x34).toNibbles // "1234"
val prefix = ByteVector(0x12).toNibbles
val result = nibbles.stripPrefix(prefix)
// remainder는 "34"를 나타냄

비교 메서드

def compareTo(that: Nibbles): Int
def <=(that: Nibbles): Boolean
def <(that: Nibbles): Boolean
def >=(that: Nibbles): Boolean
def >(that: Nibbles): Boolean

Nibbles의 사전순 비교.

compareTo 반환:

hex

def hex: String

16진수 문자열로 변환.

예제:

import org.sigilaris.core.merkle.Nibbles.{given, *}
import scodec.bits.ByteVector

val nibbles = ByteVector(0xAB, 0xCD).toNibbles
nibbles.hex // "abcd"

변환 확장

BitVector를 Nibbles로

extension (bitVector: BitVector)
  def refineToNibble: Either[String, Nibbles]
  def assumeNibbles: Nibbles

refineToNibble: 검증을 포함한 안전한 변환

assumeNibbles: 안전하지 않은 변환 (유효하지 않으면 예외 발생)

ByteVector를 Nibbles로

extension (byteVector: ByteVector)
  def toNibbles: Nibbles

ByteVector를 Nibbles로 변환 (ByteVector는 항상 8의 배수 비트를 가지므로 항상 안전).


MerkleTrieState

변경 추적 기능을 가진 Merkle Trie의 상태를 나타냄.

구조

final case class MerkleTrieState(
  root: Option[MerkleRoot],
  base: Option[MerkleRoot],
  diff: MerkleTrieStateDiff
)

필드:

생성자

object MerkleTrieState {
  def empty: MerkleTrieState
  def fromRoot(root: MerkleRoot): MerkleTrieState
  def fromRootOption(root: Option[MerkleRoot]): MerkleTrieState
}

empty: 루트나 기준이 없는 빈 상태 생성

fromRoot: 루트 해시로부터 상태 생성 (root와 base 모두 설정)

fromRootOption: 선택적 루트 해시로부터 상태 생성

예제:

import org.sigilaris.core.merkle.*
import org.sigilaris.core.crypto.Hash

val state1 = MerkleTrieState.empty

val rootHash: MerkleTrieNode.MerkleRoot = ??? // 어딘가로부터
val state2 = MerkleTrieState.fromRoot(rootHash)

메서드

rebase

def rebase(that: MerkleTrieState): Either[String, MerkleTrieState]

이 상태를 다른 상태 위에 리베이스. 두 상태가 동일한 기준을 공유할 때 차이를 결합.

매개변수:

반환:


MerkleTrie 연산

모든 연산은 이펙트풀하며 다음을 반환:

StateT[EitherT[F, String, *], MerkleTrieState, A]

이는 다음을 의미:

NodeStore

type NodeStore[F[_]] = Kleisli[EitherT[F, String, *], MerkleHash, Option[MerkleTrieNode]]

해시로 노드를 검색하는 저장소 계층. MerkleTrie 연산을 사용하려면 암묵적 인스턴스를 제공해야 함.

예제:

import cats.effect.IO
import cats.effect.unsafe.implicits.global
import cats.data.{EitherT, Kleisli}
import org.sigilaris.core.merkle.*

val store = scala.collection.mutable.Map.empty[MerkleTrieNode.MerkleHash, MerkleTrieNode]

given MerkleTrie.NodeStore[IO] = Kleisli { hash =>
  EitherT.rightT[IO, String](store.get(hash))
}

get

def get[F[_]: Monad: NodeStore](
  key: Nibbles
): StateT[EitherT[F, String, *], MerkleTrieState, Option[ByteVector]]

Merkle Trie에서 키로 값을 검색.

매개변수:

반환: 찾으면 Some(value), 아니면 None을 반환하는 상태풀 계산

예제:

import cats.effect.IO
import cats.effect.unsafe.implicits.global
import org.sigilaris.core.merkle.*
import org.sigilaris.core.merkle.Nibbles.{given, *}
import scodec.bits.ByteVector

given MerkleTrie.NodeStore[IO] = ???

val key = ByteVector(0x12, 0x34).toNibbles
val program = MerkleTrie.get[IO](key)

val (state, result) = program.run(MerkleTrieState.empty).value.unsafeRunSync() match
  case Right(r) => r
  case Left(e) => throw new Exception(e)

put

def put[F[_]: Monad: NodeStore](
  key: Nibbles,
  value: ByteVector
): StateT[EitherT[F, String, *], MerkleTrieState, Unit]

Merkle Trie에 키-값 쌍을 삽입 또는 업데이트.

매개변수:

반환: 트라이를 업데이트하는 상태풀 계산

예제:

import cats.effect.IO
import cats.effect.unsafe.implicits.global
import org.sigilaris.core.merkle.*
import org.sigilaris.core.merkle.Nibbles.{given, *}
import scodec.bits.ByteVector

given MerkleTrie.NodeStore[IO] = ???

val key = ByteVector(0x12, 0x34).toNibbles
val value = ByteVector.fromValidHex("abcd")

val program = MerkleTrie.put[IO](key, value)

val (state, _) = program.run(MerkleTrieState.empty).value.unsafeRunSync() match
  case Right(r) => r
  case Left(e) => throw new Exception(e)

// state.root는 이제 새로운 루트 해시를 포함

remove

def remove[F[_]: Monad: NodeStore](
  key: Nibbles
): StateT[EitherT[F, String, *], MerkleTrieState, Boolean]

Merkle Trie에서 키-값 쌍을 제거.

매개변수:

반환: 키를 찾아 제거하면 true, 아니면 false를 반환하는 상태풀 계산

예제:

import cats.effect.IO
import cats.effect.unsafe.implicits.global
import org.sigilaris.core.merkle.*
import org.sigilaris.core.merkle.Nibbles.{given, *}
import scodec.bits.ByteVector

given MerkleTrie.NodeStore[IO] = ???

val key = ByteVector(0x12, 0x34).toNibbles
val program = MerkleTrie.remove[IO](key)

val (state, wasRemoved) = program.run(MerkleTrieState.empty).value.unsafeRunSync() match
  case Right(r) => r
  case Left(e) => throw new Exception(e)

streamFrom

def streamFrom[F[_]: Monad: NodeStore](
  key: Nibbles
): StateT[EitherT[F, String, *], MerkleTrieState, Stream[EitherT[F, String, *], (Nibbles, ByteVector)]]

주어진 키(포함)부터 사전순으로 모든 키-값 쌍을 스트리밍.

매개변수:

반환: (key, value) 쌍의 스트림을 반환하는 상태풀 계산

예제:

import cats.effect.IO
import cats.effect.unsafe.implicits.global
import org.sigilaris.core.merkle.*
import org.sigilaris.core.merkle.Nibbles.{given, *}
import scodec.bits.ByteVector

given MerkleTrie.NodeStore[IO] = ???

val startKey = ByteVector(0x00).toNibbles
val (state, pairs) = (for
  stream <- MerkleTrie.streamFrom[IO](startKey)
yield stream).run(MerkleTrieState.empty).value.unsafeRunSync() match
  case Right((st, stream)) => (st, stream.compile.toList.value.unsafeRunSync().toOption.get)
  case Left(e) => throw new Exception(e)

reverseStreamFrom

def reverseStreamFrom[F[_]: Monad: NodeStore](
  keyPrefix: Nibbles,
  keySuffix: Option[Nibbles]
): StateT[EitherT[F, String, *], MerkleTrieState, Stream[EitherT[F, String, *], (Nibbles, ByteVector)]]

역 사전순으로 키-값 쌍을 스트리밍.

매개변수:

반환: (key, value) 쌍의 역순 스트림을 반환하는 상태풀 계산

keyPrefix로 시작하는 쌍을 반환하며, 접미사가 제공된 경우 keyPrefix + keySuffix까지 (제외) 반환.

예제:

import cats.effect.IO
import cats.effect.unsafe.implicits.global
import org.sigilaris.core.merkle.*
import org.sigilaris.core.merkle.Nibbles.{given, *}
import scodec.bits.ByteVector

given MerkleTrie.NodeStore[IO] = ???

val prefix = ByteVector(0x12).toNibbles
val (state, pairs) = (for
  stream <- MerkleTrie.reverseStreamFrom[IO](prefix, None)
yield stream).run(MerkleTrieState.empty).value.unsafeRunSync() match
  case Right((st, stream)) => (st, stream.compile.toList.value.unsafeRunSync().toOption.get)
  case Left(e) => throw new Exception(e)

헬퍼 함수

getNode

def getNode[F[_]: Monad](state: MerkleTrieState)(using NodeStore[F]): EitherT[F, String, Option[MerkleTrieNode]]

상태에서 루트 노드를 검색. 먼저 diff를 확인한 다음 노드 저장소를 확인.

getNodeAndStateRoot

def getNodeAndStateRoot[F[_]: Monad](state: MerkleTrieState)(using NodeStore[F]): EitherT[F, String, Option[(MerkleTrieNode, MerkleHash)]]

상태에서 루트 노드와 그 해시를 검색.

getCommonPrefixNibbleAndRemainders

def getCommonPrefixNibbleAndRemainders(
  nibbles0: Nibbles,
  nibbles1: Nibbles
): (Nibbles, Nibbles, Nibbles)

두 Nibbles의 공통 접두사와 나머지를 계산.

매개변수:

반환: (공통 접두사, remainder0, remainder1)

예제:

import org.sigilaris.core.merkle.*
import org.sigilaris.core.merkle.Nibbles.{given, *}
import scodec.bits.ByteVector

val n1 = ByteVector(0x12, 0x34).toNibbles // "1234"
val n2 = ByteVector(0x12, 0x56).toNibbles // "1256"

val (common, rem1, rem2) = MerkleTrie.getCommonPrefixNibbleAndRemainders(n1, n2)
// common = "12", rem1 = "34", rem2 = "56"

← Merkle Trie