infrablockchain-docs
ko
ko
  • 인프라블록체인
    • 배우기
      • 아키텍처
        • 아키텍처
        • 네트워크 참여자
        • 파라체인
          • 시스템 파라체인
      • 프로토콜
        • 시스템 토큰
        • 트랜잭션 수수료
        • Proof of Transaction
      • Substrate
        • 배우기
          • 기초 지식
            • 암호학
            • 블록체인 기본 개념
            • 합의
            • 네트워크와 노드
            • 트랜잭션과 블록 기본 사항
            • 트랜잭션 수명주기
            • 오프체인 작업
            • 라이트 클라이언트
            • Substrate를 위한 Rust
            • 라이브러리 소개
            • 아키텍처와 Rust 라이브러리
            • 파일 구조
            • 계정, 주소 및 키
            • 트랜잭션 형식
            • 난수 생성
          • 프레임
            • FRAME 팔레트
            • FRAME 매크로
            • 커스텀 팔레트
            • 팔레트 커플링
            • Origin
            • 이벤트와 에러
            • 런타임 스토리지 구조
            • 상태 전이와 스토리지
            • SCALE 인코딩
            • 트랜잭션, Weight 및 수수료
            • 런타임 API
            • 런타임 업그레이드
            • 런타임 개발
          • 계정 데이터 구조
          • 주소 형식
          • 용어집
          • cli
            • 아카이브
            • 메모리 프로파일러
            • 노드 템플릿
            • 사이드카
            • srtool
            • 서브키
            • subxt
            • try-runtime
            • tx-wrapper
          • 런타임 개발
            • 기본
              • Genesis 상태 구성하기
              • 런타임 상수 구성
              • 체인 스펙을 커스텀하기
              • 팔레트 가져오기
              • 도우미 함수 사용하기
            • 합의 모델
              • 작업 증명을 사용하는 체인 구성
              • 하이브리드 노드 생성하기
            • 오프체인 워커
              • 오프체인 HTTP 요청하기
              • 오프체인 인덱싱
              • 오프체인 로컬 스토리지
            • 팔레트 설계
              • 크라우드펀딩 구성하기
              • 스토리지 구조체 (struct) 생성하기
              • 잠금 가능한 통화 구현
              • 무작위성 적용하기
              • 느슨한 팔레트 결합 사용하기
              • 타이트한 팔레트 결합 사용하기
            • 파라체인 개발
              • HRMP 채널 추가하기
              • 로컬 파라체인 노드 추가하기
              • 릴레이 체인에 연결하기
              • 솔로 체인을 변환하기
              • 론칭 준비
              • 콜레이터 선택
              • 파라체인 업그레이드
            • 스토리지 마이그레이션
              • 기본 저장소 마이그레이션
              • 스토리지 마이그레이션 트리거
            • 테스트
              • 기본 테스트 설정하기
              • 전송 함수 테스트하기
            • 도구
              • 체인을 위한 txwrapper 생성
              • REST 엔드포인트를 사용하여 체인 데이터 가져오기
              • try-runtime 사용하기
              • Wasm 바이너리 검증하기
            • 가중치
              • 벤치마크 추가
              • 수수료 계산하기
              • 조건부 가중치 사용
              • 사용자 정의 가중치 사용하기
        • 빌드하기
          • 제작할 것을 결정하세요
          • 빌드 프로세스
          • 결정론적 런타임 빌드
          • 체인 스펙
          • Genesis 구성
          • 애플리케이션 개발
          • RPC
          • 문제 해결
        • 튜토리얼
          • 설치하기
            • 개발자 도구
            • 리눅스 개발 환경
            • macOS 개발 환경
            • Rust 툴체인
            • Rust 문제 해결 방법
            • Windows 개발 환경
          • 빠른 시작
            • 코드 탐색하기
            • 런타임 수정하기
            • 노드 시작하기
            • Substrate 한눈에 보기
          • 블록체인 구축
            • 신뢰할 수 있는 노드 추가
            • 특정 노드 승인
            • 로컬 블록체인 구축하기
            • 네트워크 시뮬레이션
            • 실행 중인 네트워크 업그레이드
          • 애플리케이션 로직 구축
            • 런타임에 팔레트 추가하기
            • 오프체인 워커 추가
            • 사용자 정의 팔레트 게시
            • 함수 호출의 출처 지정하기
            • 사용자 정의 팔레트에서 매크로 사용하기
          • 유용한 도구들
            • EVM 계정에 접근하기
            • 이더리움 통합
            • 사이드카 엔드포인트 탐색하기
            • 경량 클라이언트 노드 통합
          • 스마트 컨트랙트
            • 스마트 컨트랙트
            • 토큰 계약 작성하기
            • 스마트 컨트랙트 개발하기
            • 첫 번째 계약 준비하기
            • 스마트 컨트랙트 문제 해결
            • 값 저장을 위한 맵 사용
      • XCM
        • XCM
        • XCM 형식
    • 서비스 체인
      • 인프라DID
      • 인프라EVM
      • URAuth(Universal Resource Auth)
    • 데브 옵스
      • 체인 빌드
      • 배포
      • 모니터링
    • 튜토리얼
      • 기초
        • 시스템 토큰 관리 프로세스
        • 시스템 토큰을 트랜잭션 수수료로 사용해보기
        • 트랜잭션에 투표 포함 시키기
        • 밸리데이터 보상 받기
      • 구축하기
        • 인프라릴레이체인 구축하기
        • 파라체인 구축하기
        • 메시지 전달 채널 열기
        • XCM을 이용하여 토큰 전송하기
        • Asynchronous Backing 적용하기
      • 테스트
        • 벤치마크
        • 런타임 확인
        • 디버그
        • 테스트 네트워크에서 파라체인 시뮬레이션하기
        • 단위 테스트
      • 서비스체인
        • 인프라DID
          • 구축하기
          • 공개키 추가하기
          • 서비스 엔드포인트 등록하기
          • DID 생성하기
        • 인프라EVM
          • 구축하기
          • EVM에 자금 입금 및 인출하기
          • ERC20 토큰 컨트랙트 배포하기
          • ERC721 토큰 컨트랙트 배포하기
          • ERC1155 토큰 컨트랙트 배포하기
  • 뉴날 데이터 마켓
Powered by GitBook
On this page
  • 절차
  • 예제
  1. 인프라블록체인
  2. 배우기
  3. Substrate
  4. 배우기
  5. 런타임 개발
  6. 오프체인 워커

오프체인 로컬 스토리지

Previous오프체인 인덱싱Next팔레트 설계

Last updated 1 year ago

이 가이드는 오프체인 워커를 사용하여 검색된 데이터를 로컬 스토리지에 저장하여 나중에 액세스하는 방법입니다.

지난 섹션에서 오프체인 워커(OCW)는 블록체인 상태를 직접 수정할 수 없으므로 계산된 결과를 체인에 다시 저장하기 위해 트랜잭션을 제출해야 한다고 언급했습니다. 그러나 데이터가 체인에 저장하기에 적합하지 않지만 나중에 액세스할 수 있도록 어딘가에 저장해야 하는 경우도 있습니다. 이에는 일시적인 데이터나 계산이 완료된 후에 폐기할 수 있는 중간 계산이 포함됩니다.

이 가이드에서는 오프체인 워커에게 데이터를 전체 블록체인 네트워크 사이에서 전달하지 않고 로컬 스토리지에 데이터를 작성하도록 지시합니다. 여러 OCW에서 일관되게 액세스할 수 있는 값을 가지기 위해 스토리지 락(Storage Lock) 개념이 소개됩니다. OCW는 각 블록 생성의 끝에서 비동기적으로 실행되며 실행 시간에 제한이 없으므로 언제든지 여러 OCW 인스턴스가 초기화될 수 있습니다.

로컬 스토리지 API는 체인 상의 상응하는 API와 유사하게 get, set, mutate를 사용하여 액세스합니다. mutate는 을 사용하여 메모리 위치의 내용을 주어진 값과 비교하고, 그 값이 동일한 경우에만 해당 메모리 위치의 내용을 새로운 주어진 값으로 수정합니다. 이는 단일 원자적 작업으로 수행됩니다. 원자성은 새로운 값이 최신 정보를 기반으로 계산되었음을 보장합니다. 값이 그 동안 다른 스레드에 의해 업데이트되었다면 쓰기 작업은 실패합니다.

로컬 스토리지에 저장된 값은 네트워크 간의 합의 메커니즘을 거치지 않았으므로 노드 운영자의 조작에 노출될 수 있다는 점에 유의하십시오.

이 가이드에서는 먼저 스토리지에서 캐시로 작동하는지 확인하기 위해 스토리지를 확인합니다. 캐시된 값이 발견되면 오프체인 워커가 반환되고, 그렇지 않으면 락을 획득하고 고성능 계산을 수행한 후 스토리지에 저장합니다.

절차

  1. 팔렛의 offchain_worker 함수 훅에서 스토리지 참조를 정의합니다.

    fn offchain_worker(block_number: T::BlockNumber) {
      // 로컬 스토리지 값에 대한 참조를 생성합니다.
      // 로컬 스토리지는 모든 오프체인 워커에 공통이므로,
      // 팔렛 이름으로 항목을 먼저 추가하는 것이 좋습니다.
      let storage = StorageValueRef::persistent(b"pallet::my-storage");
    }

    위의 코드에서는 를 사용하여 영구 로컬 스토리지를 정의하고 pallet::my-storage 키로 식별합니다. 키는 str 타입이 아닌 바이트 배열 타입으로 지정됩니다. 이 로컬 스토리지는 오프체인 워커의 실행 간에 지속되고 공유됩니다.

  2. 스토리지에 캐시된 값이 있는지 확인합니다.

    fn offchain_worker(block_number: T::BlockNumber) {
      // ...
      let storage = StorageValueRef::persistent(b"pallet::my-storage");
    
      if let Ok(Some(res)) = storage.get::<u64>() {
        log::info!("cached result: {:?}", res);
        return Ok(());
      }
    }

    함수를 사용하여 결과를 가져옵니다. 이 함수는 Result<Option<T>, StorageRetrievalError> 타입을 반환합니다. 유효한 값이 있는 경우에만 관심이 있습니다. 그렇다면 Ok(())를 반환합니다. 반환된 값의 타입을 정의해야 합니다.

    set()을 사용하여 스토리지에 쓰고, mutate<T, E, F>()를 사용하여 스토리지를 원자적으로 읽고 변경합니다.

  3. 유효한 값(None)이 없거나 StorageRetrievalError가 있는 경우, 필요한 결과를 계산하고 스토리지 락을 획득합니다.

    다음과 같이 스토리지 락을 먼저 정의합니다.

    const LOCK_BLOCK_EXPIRATION: u32 = 3; // 블록 번호로 지정
    const LOCK_TIMEOUT_EXPIRATION: u64 = 10000; // 밀리초로 지정
    
    fn offchain_worker(block_number: T::BlockNumber) {
      // ...
      let storage = StorageValueRef::persistent(b"pallet::my-storage");
    
      if let Ok(Some(res)) = storage.get::<u64>() {
        log::info!("cached result: {:?}", res);
        return Ok(());
      }
    
      // 매우 고성능 계산이 여기에 있습니다.
      let val: u64 = 100 + 100;
    
      // 스토리지 락을 정의합니다.
      let mut lock = StorageLock::<BlockAndTime<Self>>::with_block_and_time_deadline(
        b"pallet::storage-lock",
        LOCK_BLOCK_EXPIRATION,
        Duration::from_millis(LOCK_TIMEOUT_EXPIRATION)
      );
    }

    위의 코드 스니펫에서는 스토리지 락이 정의됩니다. 이 함수는 락 식별자, 블록 번호 만료 및 시간 만료를 입력으로 받습니다. 위의 락은 지정된 블록 번호나 시간 경과 중 하나가 지나면 만료됩니다. 또는 로 만료 기간을 지정할 수도 있습니다.

  4. 을 사용하여 스토리지 락을 획득합니다.

    fn offchain_worker(block_number: T::BlockNumber) {
      // ...
    
      let mut lock = /* .... */;
    
      if let Ok(_guard) = lock.try_lock() {
        storage.set(&val);
      }
    }

    Result<StorageLockGuard<'a, '_, L>, <L as Lockable>::Deadline>를 반환합니다. 여기서의 메커니즘은 스토리지에 쓰기 전에 먼저 락 가드를 소유해야 한다는 것입니다. 락 가드는 한 번에 하나의 프로세스만 소유할 수 있습니다. 그런 다음 set()을 사용하여 값을 스토리지에 씁니다. set()에 전달되는 값의 데이터 타입은 위의 get<T>() 호출에서 지정한 타입과 동일해야 합니다.

  5. 마지막으로, 오프체인 워커 함수에서 반환합니다.

    전체 코드는 다음과 유사합니다.

    const LOCK_BLOCK_EXPIRATION: u32 = 3; // 블록 번호로 지정
    const LOCK_TIMEOUT_EXPIRATION: u64 = 10000; // 밀리초로 지정
    
    fn offchain_worker(block_number: T::BlockNumber) {
      let storage = StorageValueRef::persistent(b"pallet::my-storage");
    
      if let Ok(Some(res)) = storage.get::<u64>() {
        log::info!("cached result: {:?}", res);
        return Ok(());
      }
    
      // 매우 고성능 계산이 여기에 있습니다.
      let val: u64 = 100 + 100;
    
      // 스토리지 락을 정의합니다.
      let mut lock = StorageLock::<BlockAndTime<Self>>::with_block_and_time_deadline(
        b"pallet::storage-lock",
        LOCK_BLOCK_EXPIRATION,
        Duration::from_millis(LOCK_TIMEOUT_EXPIRATION)
      );
    
      if let Ok(_guard) = lock.try_lock() {
        storage.set(&val);
      }
      Ok(())
    }

예제

비교 및 교체 패턴
StorageValueRef::persistent()
get<T: Decode>()
블록 번호와 시간 만료가 지정된
블록 번호만
시간 경과
.try_lock()
Substrate의 오프체인 워커 예제 팔렛
OCW 팔렛 데모