Substrate Permissioned Network

Yechan Jeon·2022년 2월 18일
0
post-thumbnail

공공 네트워크나 허락이 필요없는 블록체인은 누구나 네트워크에 참여할 수 있지만, permissioned network에서는 오직 권한이 있는 노드들만 특정 활동(e.g. validate blocks)이 가능함.

예를 들어서 private network나 규제를 받는 데이터인 경우(헬스케어처럼) 그리고 공공 네트워크를 테스트할 때도 사용할 수 있다.


튜토리얼 목표

  1. 'node-authorization pallet'을 템플릿에 추가하기
  2. 다수의 노드를 시작하고 새로운 노드가 참여할 수 있도록 권한을 부여하기

node-authorization pallet

이 pallet는 FRAME에 내장되어있다. 각각의 노드들은 Vec<u8> 타입을 가지는 'PeerId'로 구분되고 'peerId'는 'AccountId'에 의해 소유됌.

이 pallet으로 새로운 노드에게 권한을 부여하는 방법은 두 가지가 있다.
1. 연결이 허용된 잘 알려진 노드들의 집합에 참가하는 것.
2. 특정한 노드에게 paired peer 연결을 요청하는 것. 특정한 노드는 유명한 노드일 수도 있고 평범한 노드일 수도 있음.

PeerId와 관련된 노드는 오직 하나의 소유자를 가져야만 한다. 유명한 노드의 소유자는 추가될 때부터 명시되고, 일반적인 노드라면 어느 유저나 'PeerId'의 소유자임을 주장할 수 있기 때문에, false claim을 방지하기 위해 노드를 시작하기 전에 PeerId에 대한 소유권을 주장해야한다.

그러고 난 다음, 네트워크에 노드를 연결하거나 제거할 수 있음.
추가로, 유명한 노드 사이의 연결은 항상 서로 연결되어있기로 정해져서 변경할 수 없음.
반면에 유명한 노드와 일반 노드의 연결 혹은 일반 노드끼리의 연결은 조작가능.
노드가 가장 최근 블록과 동기화 되지 않을 수도 있기 때문에 이러한 경우에는 offchain worker를 무력화 시키고 수동으로 예약 노드를 설정해야할수도 있음.

pallet 추가하기

substrate-node-template directory의 runtime/cargo.toml로 이동.

[dependencies.pallet-node-authorization]
default-features = false
git = 'https://github.com/paritytech/substrate.git'
tag = 'devhub/latest'
version = '4.0.0-dev'

#--snip--
[features]
default = ['std']
std = [
    #--snip--
    'pallet-node-authorization/std',
    #--snip--
]
  • runtime/src/lib.rs
    pallet interface를 EnsureRoot에 구성해서 sudo admin rule을 허용한다.
    node-authorization pallet에 사용할 paramter을 parameter_types! 매크로를 이용해서 설정하고 다른 내장 pallet과 동일한 방식으로 node-authorization의 Config trait을 Runtime에 implementation 해주면 된다.
/* --snip-- */

use frame_system::EnsureRoot;

/* --snip-- */

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 = ();
}

/* --snip-- */

마지막으로 construct_runtime! macro에 추가

construct_runtime!(
	pub enum Runtime where
		Block = Block,
		NodeBlock = opaque::Block,
		UncheckedExtrinsic = UncheckedExtrinsic
	{
		System: frame_system,
		NodeAuthorization: pallet_node_authorization, //이 줄 추가
		RandomnessCollectiveFlip: pallet_randomness_collective_flip,
		Timestamp: pallet_timestamp,
		Aura: pallet_aura,
		Grandpa: pallet_grandpa,
		Balances: pallet_balances,
		TransactionPayment: pallet_transaction_payment,
		Sudo: pallet_sudo,
		// Include the custom logic from the pallet-template in the runtime.
		TemplateModule: pallet_template,
	}
);

genesis storage를 pallet에 추가하기

PeerId는 bs58로 인코딩 되어있다. bs58 라이브러리를 node/cargo.toml에 추가

[dependencies]
#--snip--
bs58 = "0.4.0"
#--snip--

그 다음 node/src/chain_spec.rs에 genesis storage를 추가한다.
먼저 필요한 dependencies를 import해주고

use sp_core::OpaquePeerId; // A struct wraps Vec<u8>, represents as our `PeerId`.
use node_template_runtime::NodeAuthorizationConfig; // The genesis config that serves for our pallet.

testnet_genesis function에 아래 코드를 추가한다.
NodeAuthorizationConfig는 nodes라는 tuple의 vector인 property를 포함한다.
tuple의 첫번째 element(OpaquePeerId)는 PeerId를 사람이 읽을 수 있는 형식에서 bytes 형식으로 변환하는 것이고, 두번째는(endowed_accounts) AccountId이다
여기서는 Alice와 Bob을 사용

/// Configure initial storage state for FRAME modules.
fn testnet_genesis(
    wasm_binary: &[u8],
    initial_authorities: Vec<(AuraId, GrandpaId)>,
    root_key: AccountId,
    endowed_accounts: Vec<AccountId>,
    _enable_println: bool,
) -> GenesisConfig {

        /* --snip-- */

    /*** Add This Block Item ***/
        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()
                ),
            ],
        },

    /* --snip-- */

}

Permissioned Network 시작

Node key와 PeerId 세트

Alice - well known node

# Node Key
c12b6d18942f5ee8528c8e2baf4e147b5c5c18710926ea492d09cbd9f6c9f82a

# Peer ID, generated from node key
12D3KooWBmAwcd4PJNJvfV89HwE48nwkRmAgo8Vy3uQEyNNHBox2

# BS58 decoded Peer ID in hex:
0x0024080112201ce5f00ef6e89374afb625f1ae4c1546d31234e87e3c3f51a62b91dd6bfa57df

Bob - well known node

# 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의 sub-node

# Node Key
a99331ff4f0e0a0434a6263da0a5823ea3afcfffe590c9f3014e6cf620f2b19a

# Peer ID, generated from node key
12D3KooWPHWFrfaJzxPnqnAYAoRUyAHHKqACmEycGTVmeVhQYuZN

# BS58 decoded Peer ID in hex:
0x002408011220c81bc1d7057a1511eb9496f056f6f53cdfe0e14c8bd5ffca47c70a8d76c1326d

Alice와 Bob은 well-known이기 때문에 이미 genesis storage에 구성되었고,
Charlie 노드는 후에 well-known으로 추가할 것이다. 마지막으로 Charlie와 David의 노드 사이의 연결을 추가하는 것이 목표.

Alice와 Bob 네트워크 시작

  1. Alice
    계속해서 command line option을 사용하는 것을 알 수 있는데
    각각의 옵션에 관한 설명은 ./target/release/node-template -h을 통해서 확인할 수 있다.
./target/release/node-template \
--chain=local \
--base-path /tmp/validator1 \
--alice \
--node-key=c12b6d18942f5ee8528c8e2baf4e147b5c5c18710926ea492d09cbd9f6c9f82a \
--port 30333 \
--ws-port 9944
  1. 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
  1. Charlie를 well-known 노드로 추가
    Charlie는 Alice, Bob과 다르게 사전에 권한을 얻지 못했으므로 수동으로 추가시켜줘야한다.
./target/release/node-template \
--chain=local \
--base-path /tmp/validator3 \
--name charlie  \
--node-key=3a9d5b35b9fb4c42aafadeca046f6bf56107bd2579687f069b42646684b94d9e \
--port 30335 \
--ws-port=9946 \
--offchain-worker always

Polkadot.js를 이용하여 sudo pallet의 add_well_known_node를 통해 Charlie의 PeerId와 함께 submit하면 터미널에서 Charlie가 Alice, Bod에게 연결된 것을 확인할 수 있음.

  1. Charlie에게 Dave를 sub-node로 추가 -> Dave는 오직 Charlie에게 연결함
    일단 아래 커맨드 실행, Charlie와 마찬가지로 가능한 연결은 아직 없음
./target/release/node-template \
--chain=local \
--base-path /tmp/validator4 \
--name dave \
--node-key=a99331ff4f0e0a0434a6263da0a5823ea3afcfffe590c9f3014e6cf620f2b19a \
--port 30336 \
--ws-port 9947 \
--offchain-worker always

Dave를 Charlie에게 연결하는 과정은 공식문서 참고
성공했을 경우 아래 결과를 확인 가능
2022-02-18 20:00:24 ✨ Imported #282 (0xc2d5…b6f3)

profile
방황했습니다. 다시 웹 개발 하려고요!

0개의 댓글