6. Start a Permissioned Network

코와->코어·2022년 5월 24일
0

Substrate 공부하기

목록 보기
6/8
post-thumbnail

소개

node-authorization 팔레트를 사용해 Substrate에서 허가형 네트워크를 구성하는 방법에 대해 다룰 것

퍼블릭 블록체인 : 모두가 노드를 운영함으로써 네트워크에 참여 가능
허가형 블록체인 : 권한있는 노드들만 블록 검증하거나 거래 전파시킬 수 있음

Proof of Existence (2번 튜토리얼)을 완료했고 Substrate의 P2P 네트워킹에 익숙하다(Private Network Tutorial 5번 튜토리얼을 완료했다)고 가정할 것임


여기서 할 것

  1. 노트 템플릿에node-authorization 팔레트 추가
  2. 여러 노드들을 시작하고 새로운 노드들이 참여할 수 있도록 권한 주기

학습 목표

  • node-authorization 팔레트를 런타임에서 사용하는 방법 배우기
  • 여러 노드들로 구성된 허가형 네트워크 만드는 법 배우기

node-authorization팔레트 추가

node-authorization 팔레트는 Substrate의 FRAME에 내장된 팔레트로, 허가형 네트워크를 위한 노드 설정을 관리
각 노드는Vec<u8> 타입의 PeerId에 의해 식별됨
각 노드를 claim한 AccountId가 PeerId를 소유함

이 팔레트에서 네트워크에 참여하고 싶은 노드들에게 권한을 주는 두 가지 방법

  1. 그 사이에 어떤 연결이 허가되었는지 잘 알려진 노드들의 집합에 참여한다. 그러기 위해 거버넌스(또는 Sudo)에 의해 허가받아야 함 -> 뭔 말???

  2. 특정 노드로부터 짝 연결을 요청하기. 잘 알려진 노드일 수도, 일반 노드일 수도 있음

PeerId와 연결된 노드는 반드시 하나의 주인을 가져야 함
잘 알려진 노드의 주인은 그 노드를 추가할 때 명시됨
만약 일바 노드라면, 어떤 사용자든지 해당 노드의 주인으로써 PeerId를 등록할 수 있음
가짜 등록을 막기 위해 노드 관리자는 노드를 시작하기 전에 등록해야 하고, 따라서 뒤에 그 네트워크에 등록하는 누구에게나 PeerId를 드러냄

노드의 주인은 자기의 노드에 대해 연결을 추가하고 삭제할 수 있음
정확히 말하자면, 잘 알려진 노드들 사이의 연결은 변경할 수 없고, 언제나 서로 연결될 수 있음.
대신, 잘 알려진 노드와 일반 노드 사이 또는 두 개의 일반 노드와 서브 노드들 사이의 연결을 조절할 수 있음

The node-authorization 팔레트는 노드의 연결을 설정하기 위해 offchain worker 시스템을 통합함
offchain worker가 권한이 없는 노드들에게 기본적으로 사용불가능하도록 정확한 CLI 플래그로 offchain worker를 사용해야 함

Off-chain worker : 외부 세상과 소통하기 위한 확장된 API들에 대한 접근 권한을 가짐

  • 연산 결과를 발표하기 위해 체인에 거래를 제출하는 능력
  • worker가 외부 서비스로부터 데이터를 가져오고 접근할 수 있게 해 주는 완전기능 HTTP client
  • 선언문이나 거래를 검증하고 사인하기 위한 지역 keystore에 대한 접근권한
  • 모든 off-chain worker 사이에 공유되는 지역 key-value 데이터베이스
  • 랜덤 숫자 생성을 위한 안전한 지역적 엔트로피 소스
  • 노드의 정확한 지역 시간에 대한 접근 권한
  • 일을 쉬고 재개하는 능력
    off-chain worker의 결과물은 일반 거래 검증 대상이 아님. 어떤 정보가 체인에 들어갈지 결정하기 위해 검증 메커니즘을 반드시 구현해야 함

노드 템플릿 빌드

  1. 노드 템플릿 클론받기
git clone https://github.com/substrate-developer-hub/substrate-node-template
git checkout latest
  1. 빌드하기
cd substrate-node-template/
cargo build --release
  1. 좋아하는 에디터로 열기

node-authorization 팔레트 추가

  1. 런타임 dependencies에 팔레트 추가

runtime/Cargo.toml

pallet-node-authorization = { default-features = false, git = "https://github.com/paritytech/substrate.git", branch = "polkadot-v0.9.20" }

[features]
default = ['std']
std = [
    'pallet-node-authorization/std',
]

간단한 블록체인에서 거버넌스를 시뮬레이션해야 하므로 sudo 관리자 규칙을 만들어서 EnsureRoot에 대한 팔레트의 인터페이스를 설정할 것

runtime/src/lib.rs

use frame_system::EnsureRoot;

parameter_types! {
    pub const MaxWellKnownNodes: u32 = 8;
    pub const MaxPeerIdLength: u32 = 128;
}

impl pallet_node_authorization::Config for Runtime {
    type Event = Event;
    type MaxWellKnownNodes = MaxWellKnownNodes;
    type MaxPeerIdLength = MaxPeerIdLength;
    type AddOrigin = EnsureRoot<AccountId>;
    type RemoveOrigin = EnsureRoot<AccountId>;
    type SwapOrigin = EnsureRoot<AccountId>;
    type ResetOrigin = EnsureRoot<AccountId>;
    type WeightInfo = ();
}

construct_runtime 매크로에 우리의 팔레트를 추가하기

runtime/src/lib.rs

construct_runtime!(
    pub enum Runtime where
        Block = Block,
        NodeBlock = opaque::Block,
        UncheckedExtrinsic = UncheckedExtrinsic
    {
        NodeAuthorization: pallet_node_authorization,
    }
);

팔레트에 genesis storage 추가

node/Cargo.toml

[dependencies]
bs58 = "0.4.0"

PeerId는 bs58 형식으로 인코딩되어 있기 때문에, 해독해서 바이트 얻어내려면 bs58 라이브러리 추가


node/src/chain_spec.rs

use sp_core::OpaquePeerId;
use node_template_runtime::NodeAuthorizationConfig; 

적절한 genesis storage와 필요한 의존성 추가

fn testnet_genesis(
    wasm_binary: &[u8],
    initial_authorities: Vec<(AuraId, GrandpaId)>,
    root_key: AccountId,
    endowed_accounts: Vec<AccountId>,
    _enable_println: bool,
) -> GenesisConfig {
        node_authorization: NodeAuthorizationConfig {
            nodes: vec![
                (
                    OpaquePeerId(bs58::decode("12D3KooWBmAwcd4PJNJvfV89HwE48nwkRmAgo8Vy3uQEyNNHBox2").into_vec().unwrap()),
                    endowed_accounts[0].clone()
                ),
                (
                    OpaquePeerId(bs58::decode("12D3KooWQYV9dGMFoRzNStwpXztXaBUjtPqi6aU76ZgUriHhKust").into_vec().unwrap()),
                    endowed_accounts[1].clone()
                ),
            ],
        },
}

헬퍼 함수 testnet_genesis()에 genesis 설정 추가

NodeAuthorizationConfignodes라는 튜플의 벡터인 property를 가짐
튜플의 첫 번째 요소는 OpaquePeerId 이고 이걸 사람이 읽을 수 있는 형식에서 byte로 바꾸기 위해 bs58::decode 사용
튜플의 두 번째 요소는 AccountId 이고, 이 노드의 주인을 나타내며, 설명을 위해 제공된 Alice와 Bob 계정을 사용할 것

12D3KooWBmAwcd4PJNJvfV89HwE48nwkRmAgo8Vy3uQEyNNHBox2가 어디서 나왔는지 궁금할 텐데, 위의 사람이 읽을 수 있는 PeerId를 생성하기 위해 subkey(CLI 툴)를 사용할 수 있음

subkey generate-node-key

실행 결과는 다음과 같음:

12D3KooWBmAwcd4PJNJvfV89HwE48nwkRmAgo8Vy3uQEyNNHBox2 // this is PeerId.
c12b6d18942f5ee8528c8e2baf4e147b5c5c18710926ea492d09cbd9f6c9f82a // This is node-key.

이제 허가형 네트워크 시작할 준비 끝


허가형 네트워크 시작하기

허가형 체인을 어떻게 시작하고 새 노드를 추가하는지 알아볼 것임

먼저 다 잘 컴파일 되는지 확인:

cargo build --release

4개의 노드를 시작할 것 : 3개의 잘 알려진 노드로 주인에게 허가받고 블록을 검증하는 것과, 선택된 잘 알려진 노드로부터 읽기 전용 권한만을 갖는 1개의 서브노드

노드 키와 PeerID 얻기

Alice의 잘 알려진 노드:

# Node Key
c12b6d18942f5ee8528c8e2baf4e147b5c5c18710926ea492d09cbd9f6c9f82a

# Peer ID, generated from node key
12D3KooWBmAwcd4PJNJvfV89HwE48nwkRmAgo8Vy3uQEyNNHBox2

# BS58 decoded Peer ID in hex:
0x0024080112201ce5f00ef6e89374afb625f1ae4c1546d31234e87e3c3f51a62b91dd6bfa57df

Bob의 잘 알려진 노드

# Node Key
6ce3be907dbcabf20a9a5a60a712b4256a54196000a8ed4050d352bc113f8c58

# Peer ID, generated from node key
12D3KooWQYV9dGMFoRzNStwpXztXaBUjtPqi6aU76ZgUriHhKust

# BS58 decoded Peer ID in hex:
0x002408011220dacde7714d8551f674b8bb4b54239383c76a2b286fa436e93b2b7eb226bf4de7

Charlie의 일반 노드

# Node Key
3a9d5b35b9fb4c42aafadeca046f6bf56107bd2579687f069b42646684b94d9e

# Peer ID, generated from node key
12D3KooWJvyP3VJYymTqG7eH4PM5rN4T2agk5cdNCfNymAqwqcvZ

# BS58 decoded Peer ID in hex:
0x002408011220876a7b4984f98006dc8d666e28b60de307309835d775e7755cc770328cdacf2e

Dave의 (Charlie에 대한) 서브노드

# Node Key
a99331ff4f0e0a0434a6263da0a5823ea3afcfffe590c9f3014e6cf620f2b19a

# Peer ID, generated from node key
12D3KooWPHWFrfaJzxPnqnAYAoRUyAHHKqACmEycGTVmeVhQYuZN

# BS58 decoded Peer ID in hex:
0x002408011220c81bc1d7057a1511eb9496f056f6f53cdfe0e14c8bd5ffca47c70a8d76c1326d

Alice와 Bob의 노드는 미리 genesis storage에 잘 알려진 노드로 설정해둠.
앞으로 Charlie의 노드를 잘 알려진 노드로 추가하고 Charlie의 노드와 Dave의 노드 사이의 연결을 Dave의 노드를 잘 알려진 노드로 만들지 않으면서 추가할 것

Alice와 Bob으로 네트워크 시작

먼저 Alice 노드 시작:

./target/release/node-template \
--chain=local \
--base-path /tmp/validator1 \
--alice \
--node-key=c12b6d18942f5ee8528c8e2baf4e147b5c5c18710926ea492d09cbd9f6c9f82a \
--port 30333 \
--ws-port 9944

여기서 --node-key를 사용해 네트워크 연결의 보안을 위해 사용할 키를 명시하고 있음
이 키는 위에 있는 사람이 읽을 수 있는 PeerId를 생성하는 데에도 내부적으로 사용됨

  • --chain=local : --dev랑은 다른 로컬 테스트넷을 위해 사용
  • --alice : 노드에게 alice라는 이름을 붙여주면서 누가 주인이고 블록을 완성할 것인지에 대한 권한을 줌
  • --port : P2P 연결을 위한 포트 할당
  • --ws-port : Websocket 연결을 위한 수신 포트 할당

Bob 노드 시작

# In a new terminal, leave Alice running
./target/release/node-template \
--chain=local \
--base-path /tmp/validator2 \
--bob \
--node-key=6ce3be907dbcabf20a9a5a60a712b4256a54196000a8ed4050d352bc113f8c58 \
--port 30334 \
--ws-port 9945

두 개의 노드 다 실행시키면 터미널 로그로 새로운 블록들이 생성되고 완료되는 걸 볼 수 있음
이제 polkadot.js app으로 잘 알려진 노드들을 확인해볼 것
로컬 노드 중 하나를 127.0.0.1:9944127.0.0.1:9945로 바꾸는 거 잊지 말기

Developer page로 가서,Chain State sub-tab에서 nodeAuthorization 팔레트의 wellKnownNodes storage에 저장된 데이터 확인
Alice와 Bob 노드의 peer id가 0x가 붙은 16진수 형식으로 보일 것임

스토리지에 We can also check the owner of one node by querying the storage owners 스토리지에 노드의 peer id를 입력값으로 쿼리 날려서 주인의 계정 주소를 얻음으로써 노드의 주인을 확인할 수 있음

또다른 잘 알려진 노드 추가

Charlie 노드 시작

./target/release/node-template \
--chain=local \
--base-path /tmp/validator3 \
--name charlie  \
--node-key=3a9d5b35b9fb4c42aafadeca046f6bf56107bd2579687f069b42646684b94d9e \
--port 30335 \
--ws-port=9946 \
--offchain-worker always

시작한 뒤에도 이 노드에 연결된 peer가 없는 것을 볼 수 있을 것임
이건 허가형 네트워크이기 때문에 연결되기 위해선 허가를 받아야 함!
Alice와 Bob은 처음에 chain_spec.rs에서 미리 설정해뒀음

거버넌스를 위해 sudo 팔레트를 사용하고 있어서 노드를 추가하기 위해node-authorization 팔레트가 제공하는add_well_known_node dispatch를 sudo 호출할 수 있다는 걸 기억해라

Developer pageSudo tab으로 가서 Charlie를 주인으로 해서 Charlie의 16진수 peer id를 넣고 nodeAuthorizationadd_well_known_node 을호출
이 호출에 대해 Alice가 유효한 sudo origin이라는 거에 주목해라

이 거래가 블록에 포함된 뒤, Charlie의 노드가 Alice와 Bob의 노드에 연결되고 블록을 맞춰 나가는 걸 불 수 있을 것임
세 노드들이 서로를 찾을 수 있는 이유는 mDNS discovery 메커니즘이 로컬 네트워크에 기본 설정으로 사용가능하기 때문

이제 모든 블록들을 같이 검증하는 3개의 잘 알려진 노드들이 있음

Dave를 Charlie의 서브노드로 추가

이제 Dave의 노드를 잘 알려진 노드가 아니라 Charlie의 "서브 노드"로 추가할 것
Dave는 네트워크에 접근하기 위해 오직 Charlie에게만 연결할 수 있음
보안 기능 때문임: Charlie는 어떤 연결된 서브노드에 대해 혼자 책임을 짐
제거될 때 David에 대한 접근 권한은 한 곳에서만 하면 됨

Dave의 노드 시작

./target/release/node-template \
--chain=local \
--base-path /tmp/validator4 \
--name dave \
--node-key=a99331ff4f0e0a0434a6263da0a5823ea3afcfffe590c9f3014e6cf620f2b19a \
--port 30336 \
--ws-port 9947 \
--offchain-worker always

시작하면, 가능한 연결이 없을 것임
먼저, Charlie가 Dave의 노드로부터 연결을 허용하기 위해 노드 설정해줘야 함

Developer Extrinsics page에 가서 Charlie 계정으로 addConnections extrinsic 호출
peerId는 Charlie 노드의 16진수 peer id임.
connections는 Charlie의 노드에 대해 허용된 노드들의 리스트인데, 우리는 Dave 노드만 추가할 것임

이제 Chalie의 노드로부터 연결을 허락받기 위해 Dave가 노드 설정해야 함
그 전에 너무 늦기 않았기를 바라면서 Dave의 노드를 먼저 등록해야 함!

비슷하게, Dave는 Charlie 노드로부터 연결 추가할 수 있음

이제 Dave가 블록들을 따라잡고 있고, 오직 Chalie만을 peer로 갖는 걸 볼 수 있음! Chalie와 바로 연결되지 않는 경우에는 Dave의 노드를 다시 시작하면 됨

remove_well_known_node이나 remove_connections 등의 extrinsic으로도 놀아보기


다음 단계

Private Network Tutorial 완료하기
Subkey tool 더 알아보기

profile
풀스택 웹개발자👩‍💻✨️

0개의 댓글