Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing Proof-of-Transaction #8

Closed
cocoyoon opened this issue Mar 8, 2023 · 3 comments
Closed

Implementing Proof-of-Transaction #8

cocoyoon opened this issue Mar 8, 2023 · 3 comments
Assignees
Labels
enhancement New feature or request

Comments

@cocoyoon
Copy link
Contributor

cocoyoon commented Mar 8, 2023

Proof of Transaction

트랜잭션을 많이 일으키는 서비스 제공자 혹은 parachain 을 운영하는 어떠한 계정이 어떠한 네트워크의 블록 생성자, 즉 validator를 선출해내는 컨센서스이다. 트랜잭션 안에 투표를 첨가하여 특정 시점에 수집된 투표에 대한 집계를 통해 validator 를 선별해내며 이를 Transaction-as-a-Vote 라 한다.

Transaction As a Vote

인프라 블록스페이스의 validatorTransaction-as-a-Vote 에 의해 선출된다. 트랜잭션 안에 투표를 하게 되고 특정 시점에 해당 투표를 기반으로 validator가 선정이 된다.

  • 트랜잭션마다 validator 에 대한 투표 정보를 포함시킨다
  • 블록체인 트랜잭션을 많이 일으키는 서비스의 계정이 블록 생성자로 선출되고 법정화폐 기반 트랜잭션 수수료를 보상으로 받는다

Overview

https://i.imgur.com/9EylC2B.png

인프라 블록스페이스의 컨센서스는 Proof-of-Transaction(PoT) 기반으로 이루어진다. 패러체인 블록안에 포함된 트랜잭션 안에 특정 validator 에 대한 투표 정보를 포함시켜 인프라 블록체인(패러체인)의 Proof-of-Validity(PoV) 에 포함되어 릴레이 체인으로 전달된다.릴레이 체인에서는 해당 PoV 를 검증하고 (1) 트랜잭션 안에 포함된 투표 정보를 수집한 후 (2) 각 패러체인들로부터 들어온 투표 정보들을 자신의 상태에 반영한다. 릴레이 체인의 특정 시점이 지나면 집계한 투표를 기반으로 validator set 을 변경하게 된다.

Implementation

  • Parachain

    인프라 블록스페이스에서 Relay Chain 과 연결되어 shared security 를 형성하여 특정 서비스를 수행하는 블록체인 네트워크이다. Transaction-as-a-Vote 를 수행하기 위해 parachain 으로 들어오는 트랜잭션 메타데이터를 확장함으로써 트랜잭션안에 투표 정보를 넣을 수 있게 된다. 기본적인 트랜잭션 구조에서 인프라 블록스페이스의 parachain 은 투표 정보까지 인코딩되어 Runtime 으로 들어오게된다. Collator 는 해당 트랜잭션이 포함된 블록을 생성하고 PoV(Proof-of-Validity)형태로 Relay Chain 에 전달하게 된다.

    pov.png

    PoV 는 ParachainBlockData 타입이 SCALE Codec 으로 인코딩된 형태이다.

    pub struct ParachainBlockData = {
    	pub header,
    	pub extrinsics,
    	pub storage_proof
    };
    pub struct Pov: Vec<u8> = BlockData(ParachainBlockdata.encode());
  • Relay Chain

    인프라 블록스페이스에스에서 parachain 블록을 검증하고 투표 정보를 수집하여 자체 거버넌스를 이루는 체인이다. PoT(Proof-of-Transaction) 컨센서스를 위한 단계로 크게 네가지로 나뉠 수 있다. Parachain 블록을 수집하기 위한 (1) PoV 해부(deconstructing PoV), extrinsic 안에 포함된 (2) 투표 정보를 수집(Collecting Vote) 하여 CandidateReceipt(CCR) 에 포함, Relay Chain 블록 생성자가 수집된 (3) 투표를 집계하여(Aggregating Vote) 상태를 변경 , 특정 시점에 집계된 투표를 통하여 (4) Validator 그룹을 변경 하여 인프라 블록스페이스 컨센서스 구축을 목표로 한다

    1. Deconstructing PoV

      Relay Chain 으로 들어온 PoV 를 디코딩하는 단계이다. 이를 위해서 Relay Chain 은 ParachainBlockData 타입을 정확하게 알고 있어야한다.

      let pbd = ParachainBlockData::decode(pov);
      let (_header, **extrinsics**, _storage_proof) = pbd.deconstruct();
    2. Collect Vote

      Parachain 에 할당된 validator 들에 의해 parachain 블록이 검증된 이후 Extrinsic 안에 포함되어 있는 v투표 정보를 수집하는 단계이다. 블록안에 포함되어 있는 Extrinsics 는 Vec<Block::Extrinsic> 타입으로 Block::Extrinsic 은 런타임에서 UncheckedExtrinsic 타입으로 정의되어 있다. 해당 타입안의 Extra 에 투표 정보가 포함되어있다.

      // Substrate Block 타입
      pub type Block = generic::Block<Header, UncheckedExtrinsic>;
      pub struct Block<Header, Extrinsic>{
      	type Extrinsic = Extrinsic;
      	type Header = Header;
      	..
      }
      
      // Substrate Unchecked Extrinsic 타입
      pub struct UncheckedExtrinsic<Address, Call, Signature, Extra> {
      	pub signature: Option<(Address, Signature, Extra)>,
      	pub function: Call
      } 
      
      // 블록안에 포함되어 있는 Extrinsics 들을 iterator 로 접근
      let mut votes: Vec<(AccountId, Weight)> = vec![];
      **extrinsics**.iter().map(|uxt| {
      	let vote_info: (AccountId, Weight) = get_vote_info(uxt);
      	votes.push(vote);
      });
      
      // Relay Chain 안에서 candidate 블록에 대한 검증 결과물
      let candidate_receipt = CandidateReceipt {
      	..,
      	votes
      };

    How to get vote info?

    Runtime 에서 Extrinsic 을 실행하는 로직을 차용한다. Extrinsic 안에 있는 Extra 타입은 SignedExtension trait 를 구현한 타입으로써 pre_dispatch() 메소드를 가지고 있고 해당 메소드는 Pre 타입을 반환한다.

    pub fn get_vote_info(uxt: Block::Extrinsic) -> Vote
    where 
    	Block::Extrinsic: Checkable
    {
    	// UncheckedExtrinsic 을 CheckedExtrinsic 으로 변경
    	let xt = uxt.check(); 
    	// Extrinsic 의 weight 
    	let dispatch_info = xt.get_dispatch_info();
    	// Extrinsic 을 실행
    	VoteExtractor::get_vote_info(xt, dispatch_info);
    }
    
    pub trait VoteExtractor {
    	fn get_vote_info<Extra: SignedExtension>(self, info: DispatchInfo) -> Option<VoteInfo> {
    		if let Some(_, extra) = self.signed {
    			// `pre_dispatch` 의 반환값
    			let pre = Extra::pre_dispatch()
    			// Pattern matching
    			match pre {
    				// Vote 타입이면 추출
    				Vote { who } => {
    					// (Vote 대상자, Weight) 를 담은 tuple 을 반환
    					Some(<(who, info.weight)>)
    				},
    				// Vote 타입이 아니면 None 을 반환
    				_ => return None
    			}
    		} else {
    			// Signed Transaction 이 아니면 None 을 반환 
    			return None
    		}
    	}
    }
    1. Aggregating Vote

    각 parachain 당 생성된 CandidateReceipt 의 투표 정보를 기반으로 Relay Chain 블록 생성자가 이를 취합하여 Relay Chain 상태를 변경하는 단계이다. Relay Chain 런타임 상에 PoT pallet 을 구현하여 storage 상태를 변경한다.

    // runtimes/parachain/inclusion/mod.rs
    // 각 parachain 당 생성된 ccr 을 기반으로 Relay Chain 상태를 변경
    fn enact_candidate(receipt: CommittedCandidateReceipt<T::Hash>, ..) {
    	..
    	let mut weight = T::DbWeight::get().reads_writes(1, 0);
    	weight = <pot::Pallet<T>>::set_vote_info(
    		receipt.descriptor.votes
    	);
    	Self::deposit_event(CandidateIncluded(
    			plain,
    			commitments.head_data.clone(),
    			core_index,
    			backing_group,
    	));
    }
    
    // runtimes/parachain/pot/lib.rs
    // 투표 정보를 반영할 PoT pallet를 구현
    mod pallet {
    	.. 
    	pub type VoteInfo = StorageMap;
    
    	pub enum Event {
    		..
    		VoteIncluded { who: AccountId, weight: Weight } 
      }
    }
    
    impl pallet {
    	fn set_vote_info(votes: Vec<(AccountId, Weight)> {
    		let mut vote_info = VoteInfo::get();
    		vote_info.
    	}
    }
    1. Handling Session
@cocoyoon cocoyoon added the enhancement New feature or request label Mar 8, 2023
@cocoyoon
Copy link
Contributor Author

cocoyoon commented Mar 9, 2023

아직 작성중이지만 리뷰 요청드립니다. @bezalel @architectophile @sweatpotato13

@sweatpotato13
Copy link
Contributor

Extrinsic Type format 변경으로 인한 Encode 문제는 아래 페이지가 참고가 될 것 같습니다.

https://substrate.stackexchange.com/questions/471/what-are-the-steps-for-encoding-an-extrinsic-in-substrate

This was referenced Mar 13, 2023
@cocoyoon
Copy link
Contributor Author

@energyGiver

pub struct Vote<T: Config> {
    #[codec(compact)]
    voting: Option<VotingInfo<T::AccoundData, T::Weight>>
}

Struct 안에 Struct 가 있으면 client 입장에서 번거로울 수 있으니 우선은 이런 식으로 하는게 좋아보입니다. WeightDispatchResult에서 가져와야 합니다.

pub struct Vote<T: Config> {
    who: T::AccountId,
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

3 participants