Skip to content
Chanhyuck Ko edited this page May 22, 2025 · 10 revisions

이 문서에서는 Libplanet에서 제공하는 IStoreIStateStore 인터페이스의 구현체에 대해서 설명합니다.

IStore

인터페이스

IStore 인터페이스를 구현하는 구현체들은 반드시 다음의 함수들을 구현합니다. 코드

  • ListChainIds: 스토어에 저장된 체인 ID들을 반환합니다.
  • DeleteChainId: 스토어에서 체인 ID를 제거합니다.
  • GetCanonicalChainId: Canonical 체인의 ID를 반환합니다.
  • SetCanonicalChainId: 특정 체인 ID를 Canonical 체인으로 설정합니다.
  • CountIndex: 인자로 전달한 체인 ID의 체인에 몇 개의 블록이 있는지 세어 반환합니다.
  • IterateIndexes: 인자로 전달한 체인 ID의 체인의 offset 인덱스의 블록부터 limit 개수의 블록 해시들을 반환합니다. 인덱스가 작은 것 부터 높은 것 순으로 정렬되어 있습니다.
  • IndexBlockHash: 인자로 전달한 체인 ID의 체인의 index 블록의 해시를 반환합니다.
  • AppendIndex: 인자로 전달한 블록해시를 체인 ID의 체인에 붙이고 그 인덱스를 반환합니다.
  • ForkBlockIndexes: 체인을 포크합니다. 사용하지 않는 것을 권장합니다.
  • GetTransaction: TxId에 대응하는 트랜잭션을 스토어에서 찾아 반환합니다.
  • PutTransaction: 트랜잭션을 스토어에 저장합니다.
  • IterateBlockHashes: 체인에 포함 여부와 관계없이 모든 블록들의 해시를 반환합니다.
  • GetBlock: 블록해시에 대응하는 블록을 스토어에서 찾아 반환합니다.
  • GetBlockIndex: 블록해시에 대응하는 블록 인덱스를 스토어에서 찾아 반환합니다.
  • GetBlockDigest: 블록해시에 대응하는 블록 다이제스트를 스토어에서 찾아 반환합니다. 블록 다이제스트는 블록에서 트랜잭션의 내용물을 제거한, 스토리지 저장을 위한 자료 구조입니다.
  • PutBlock: 블록을 스토어에 저장합니다. 내부적으로 블록의 트랜잭션들 역시 PutTransaction 메서드를 호출하여 스토어에 별도 Key로 저장합니다. 이미 해당 블록이 스토어에 저장되어 있다면 아무 일도 일어나지 않습니다.
  • DeleteBlock: 블록을 스토어에서 지웁니다. 단순히 스토어에서 지우는 작업만 진행하고 Nonce 등의 정보를 업데이트 하지 않습니다.
  • ContainsBlock: 스토어에 해당 블록이 저장되어있는지 여부를 반환합니다.
  • PutTxExecution: TxExecution 을 스토어에 저장합니다. 중복된 값을 경고 없이 덮어씌웁니다.
  • GetTxExecution: 특정 블록의 트랜잭션의 실행 결과를 반환합니다. 저장된 값이 없다면 null 값이 반환됩니다.
  • PutTxIdBlockHashIndex: TxId의 트랜잭션이 어떤 블록에 포함되어있는지 정보를 저장합니다.
  • GetFirstTxIdBlockHashIndex: TxId의 트랜잭션이 최초로 포함된 블록의 블록해시 값을 반환합니다.
  • IterateTxIdBlockHashIndex: TxId의 트랜잭션이 포함된 블록의 블록해시를 모두 반환합니다.
  • DeleteTxIdBlockHashIndex: TxId이 어떤 블록에 포함되어있는 여부를 기록하는 테이블에서 해당 블록해시를 제거합니다.
  • ListTxNonces: 체인 Id에서 사용하는 논스테이블을 반환합니다.
  • GetTxNonce: 체인 Id의 체인에서 특정 Address의 현재 논스를 반환합니다. 새 트랜잭션을 만들 때 해당 값을 그대로 사용합니다.
  • IncreaseTxNonce: 체인 Id에서 특정 Address의 논스를 주어진 만큼 증가시킵니다. 음수 값을 할당하면 감소시킵니다.
  • ContainsTransaction: TxId의 트랜잭션이 스토어에 포함되어있는지 여부를 반환합니다.
  • CountBlocks: 체인과 관계없이 스토어에 저장된 모든 블록의 갯수를 반환합니다.
  • ForkTxNonces: 논스 테이블을 포크합니다. 사용하지 않는 것을 권장합니다.
  • PruneOutdatedChains: 현재 Canonical 체인에서 참조하지 않는 체인들을 모두 제거합니다.
  • GetChainBlockCommit: 노드가 수집한 체인 Id의 가장 마지막 블록커밋을 반환합니다.
  • PutChainBlockCommit: 노드가 수집한 체인 Id의 블록 커밋을 저장합니다.
  • GetBlockCommit: 블록에 저장된 커밋을 스토어에서 가져와 반환합니다.
  • PutBlockCommit: 블록커밋을 스토어에 저장합니다.
  • DeleteBlockCommit: 블록커밋 정보를 스토어에서 제거합니다.
  • GetBlockCommitHashes: 스토어에 저장된 모든 블록커밋의 블록해시 값들을 가져옵니다.
  • IteratePendingEvidenceIds, GetPendingEvidence, GetCommittedEvidence, PutPendingEvidence, PutCommittedEvidence, DeletePendingEvidence, DeleteCommittedEvidence, ContainsPendingEvidence, ContainsCommittedEvidence: 위 메서드들은 합의 과정 도중 다른 밸리데이터가 위반한 행위들을 스토어에 관리합니다.

RocksDBStore

Libplanet은 IStore 의 구현체를 몇 가지 제공하지만, 그 중 권장하는 것은 RocksDBStore 입니다. RocksDB는 고성능의 NoSQL Key-Value 스토리지이고, libplanet에서는 최적화를 위해 자주 사용하는 값들에 대한 캐싱을 진행 사용하고 있습니다.

상세 구현은 해당 소스 코드를 참고해주세요.

IStateStore

인터페이스

IStateStore 인터페이스를 구현하는 구현체들은 반드시 다음의 함수들을 구현합니다. 코드

  • GetStateRoot: 인자로 전달한 StateRootHash 에 대응하는 트라이를 반환합니다.
  • Commit: 인자로 전달한 트라이를 스토어에 저장합니다.

트라이의 변경은 ITrie 인터페이스의 메서드들을 이용해 할 수 있습니다. Libplanet은 이 ITrie를 감싸는 두 자료 구조인 IWorldStateIAccountState를 제공합니다. 액션에서는 이 두 인터페이스 형태로 상태를 관리합니다.

IAction.Execute() 의 인자인 IActionContextIActionContext.PreviousStateIWorld를 반환하고, IWorldAddress를 키로, IAccount를 값으로 갖는 Dictionary의 자료 구조 형태를 가지고 있습니다. 그리고 IAccount에서 한 번 더 Address를 키로 참조하여 실제 상태를 조회하고, 값을 변경할 수 있습니다. 아래 코드는 실제 사용 예시입니다.

DumbAction.cs#L83-L124

        public IWorld Execute(IActionContext context)
        {
            IWorld world = context.PreviousState;

            if (Append is { } append)
            {
                IAccount account = world.GetAccount(ReservedAddresses.LegacyAccount);
                string? items = (Text?)account.GetState(append.At);
                items = items is null ? append.Item : $"{items},{append.Item}";
                account = account.SetState(append.At, (Text)items!);
                world = world.SetAccount(ReservedAddresses.LegacyAccount, account);
            }

            if (Transfer is { } transfer)
            {
                world = (transfer.From, transfer.To) switch
                {
                    (Address from, Address to) => world.TransferAsset(
                        context,
                        sender: from,
                        recipient: to,
                        value: FungibleAssetValue.FromRawValue(DumbCurrency, transfer.Amount)),
                    (null, Address to) => world.MintAsset(
                        context,
                        recipient: to,
                        value: FungibleAssetValue.FromRawValue(DumbCurrency, transfer.Amount)),
                    (Address from, null) => world.BurnAsset(
                        context,
                        owner: from,
                        value: FungibleAssetValue.FromRawValue(DumbCurrency, transfer.Amount)),
                    _ => throw new ArgumentException(
                        $"Both From and To cannot be null for {transfer}"),
                };
            }

            if (Validators is { } validators)
            {
                world = world.SetValidatorSet(new ValidatorSet(validators.ToList()));
            }

            return world;
        }

ActionEvaluator에서는 액션을 실행할 때 내부적으로 IAction.Execute를 실행하여 상태를 변경하고, 반환된 IWorld 객체를 ITrie 형태로 다시 변환하여 반환합니다. 그렇게 변경된 ITrie의 루트 해시와 해당 트리를 스토어에 Commit 하여 상태를 저장합니다.

TrieStateStore

Libplanet은 IStateStore 의 구현체로서 Merkle-Patricia trie (이하 MPT) 베이스의 TrieStateStore 를 제공하고 있습니다. 내부적으로는 역시 RocksDB 베이스의 RocksDBKeyValueStore 를 사용하며, 자세한 구현은 MPT의 이해가 필요합니다.

MPT는 이더리움에서 상태를 저장하기 위한 자료 구조로서 일부만 변화했을 때 전체가 크게 바뀌지 않는 특징을 가지고 있고, 변화를 추적하기 용이합니다. 보다 자세한 설명은 이더리움 공식 문서구현체를 참고해주세요.

Clone this wiki locally