OrbitsNetwork

2019.07 월간 보고서(OrbitsNetwork)

목차

  1. Network Layer에서의 Super Node 운영 및 역할

  2. 하이퍼레저 패브릭(HyperLedger Fabric) 분석 및 활용

Network Layer에서의 Super Node 운영 및 역할

2019 Q3

  • Planet Wallet Launching

  • Network Layer Development

GBT Protocol 로드맵 상 2019년 3분기를 목표로 Network Layer 개발을 진행하고 있습니다. Super Node의 운영 및 역할에 대해 기존 프로젝트들을 통해 장단점을 분석해나가고 있습니다.

Orbits Network에서 Super Node의 역할은 모든 분산원장을 보관하고, 누적된 신뢰도를 바탕으로 선의의 Micro Node를 선별하며 이를 통해 효율적인 검증을 가능하게 합니다.

1. 소스파일 저장공간 제공

  • Super Node는 Dapp 소스파일을 저장할 수 있는 공간을 제공합니다. 소스파일은 오프체인으로 저장됩니다.

  • 개별 Super Node가 저장하는 파일은 선별 Super Node에 의해 결정됩니다. 보상은 저장 용량 단위에 비례하여 받기 때문에 이 과정에서 선택의 문제는 없습니다.

2. Micro Node에게 소스파일 제공

  • Super Node는 자신이 가진 소스파일을 Micro Node가 필요로 할 때 제공하여야 합니다.

3. Micro Node 검증풀 생성

  • Super Node는 Micro Node의 트랜잭션을 검증할 노드를 일정 주기마다 랜덤으로 선정합니다.

  • Super Node는 Micro Node로부터 발생한 트랜잭션을 정렬하여 선정된 검증풀에 전파합니다.

  • 선별 Super Node는 검증풀 생성을 위해 개별 Micro Node의 온/오프라인 상태를 점검합니다.

HyperLedger Fabric(하이퍼레저 패브릭)의 분석 및 활용

하이퍼레저 패브릭에서는 트랜잭션의 생성부터 합의하는 과정까지 단계별로 분리하여 처리할 수 있습니다.

첫 번째 실행(Execute) 단계에서는 트랜잭션을 실행하고 결과값을 검증하는 작업을 수행합니다.

두 번째 단계인 정렬(Order) 단계에서는 실행 단계에서 검증이 끝난 트랜잭션을 취합하여 순서에 맞게 정렬한 후 블록을 생성하는 작업을 수행합니다.

마지막으로, 검증(Validation) 과정에서는 블록에 포함된 모든 트랜잭션에 대한 결과값 검증을 수행하고, 각종 디지털 인증서 등을 확인한 후 이상이 없을 시 최신 블록을 업데이트하게 됩니다.

이처럼 작업을 분리하여 처리하게 되면 트랜잭션을 실행하고 검증하는 노드와 트랜잭션을 정렬하는 노드의 부하를 줄일 수 있고, 동시에 두 가지 이상의 작업을 수행하는 병렬 처리가 가능하기 때문에 시스템의 성능 또한 향상하게 됩니다.

하이퍼레저 패브릭은 시스템 구축 시 인증, 합의 알고리즘, 암호화 등의 기능을 참여자들이 원하는 형태로 선택해서 블록체인을 운영할 수 있도록 모듈화된 디자인을 지원합니다. 이와 같이 모듈화된 디자인은 하이퍼레저 패브릭 블록체인을 다양한 비즈니스 모델에 맞추어 개발할 수 있는 유연성을 제공합니다.

1. Endorsement Policy(보증정책)

보증정책(Endorsement Policy)은 트랜잭션을 생성하는 클라이언트(분산 애플리케이션)와 peer간에 작용합니다. 트랜잭션이 블록에 포함되기 위해서는 보증 정책에 지정된 peer의 허가를 받아야만 하는데, 만약 트랜잭션이 보증 정책을 충족시키지 못한다면 peer가 블록을 검증하는 과정에서 해당 트랜잭션을 블록에 포함하지 않습니다.

정확하게는 보증받지 못했다는 태그(invalid tag)와 함께 트랜잭션을 블록에 기록하고, 당연히 해당 트랜잭션은 앞으로의 거래에 전혀 영향을 미치지 않습니다.

다음 예제를 통해 보증 정책을 어떻게 설정할 수 있는지 알아보겠습니다. 우선, 다음과 같이 7대의 peer가 구성되어 있다고 가정해 봅시다.

보증 그룹 = {peer1, peer2, peer3, peer4, peer5, peer6, peer7}

이러한 조건에서 Endorsement Policy를 다음과 같이 설정할 수 있습니다.

  • 보증 그룹의 모든 peer의 디지털 인증서를 획득해야 함.

  • 보증 그룹의 peer 중 1대의 peer의 디지털 인증서를 획득해야 함.

  • {peer1 OR peer2} AND {any two of peer5, peer6, peer7}

다음과 같이 그룹 구성원에 가중치를 두고 보증 정책을 설정할 수도 있습니다.

보증 그룹 = {peer1 = 15, peer2 = 10, peer3 = 25, peer4 = 20, peer5 = 10, peer6 = 10, peer7 = 10}

  • 가중치 합계 50 이상의 peer들로부터 디지털 인증서를 획득해야 함.

  • {peer1 OR peer3} AND {가중치 40 이상}

이와 같이 하이러레저 페브릭 블록체인 참여자들은 다양한 방식의 보증 정책 설정을 통해 비즈니스의 보안성, 신뢰성 등을 강화할 수 있습니다.

package endorsement
import (
"fmt"
"testing"
"github.com/golang/protobuf/proto"
"github.com/hyperledger/fabric/common/chaincode"
"github.com/hyperledger/fabric/common/policies"
"github.com/hyperledger/fabric/common/policies/inquire"
"github.com/hyperledger/fabric/gossip/api"
"github.com/hyperledger/fabric/gossip/common"
"github.com/hyperledger/fabric/gossip/discovery"
discoveryprotos "github.com/hyperledger/fabric/protos/discovery"
"github.com/hyperledger/fabric/protos/gossip"
"github.com/hyperledger/fabric/protos/msp"
"github.com/hyperledger/fabric/protos/utils"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
)
var pkiID2MSPID = map[string]string{
"p0": "Org0MSP",
"p1": "Org1MSP",
"p2": "Org2MSP",
"p3": "Org3MSP",
"p4": "Org4MSP",
"p5": "Org5MSP",
"p6": "Org6MSP",
"p7": "Org7MSP",
"p8": "Org8MSP",
"p9": "Org9MSP",
"p10": "Org10MSP",
"p11": "Org11MSP",
"p12": "Org12MSP",
"p13": "Org13MSP",
"p14": "Org14MSP",
"p15": "Org15MSP",
}
... 중략 ...
func TestPeersAuthorizedByCriteria(t *testing.T) {
cc1 := "cc1"
cc2 := "cc2"
members := peerSet{
newPeer(0).withChaincode(cc1, "1.0"),
newPeer(3).withChaincode(cc1, "1.0"),
newPeer(6).withChaincode(cc1, "1.0"),
newPeer(9).withChaincode(cc1, "1.0"),
newPeer(12).withChaincode(cc1, "1.0"),
}.toMembers()
members2 := append(discovery.Members{}, members...)
members2 = append(members2, peerSet{newPeer(13).withChaincode(cc1, "1.1").withChaincode(cc2, "1.0")}.toMembers()...)
members2 = append(members2, peerSet{newPeer(14).withChaincode(cc1, "1.1")}.toMembers()...)
members2 = append(members2, peerSet{newPeer(15).withChaincode(cc2, "1.0")}.toMembers()...)
alivePeers := peerSet{
newPeer(0),
newPeer(2),
newPeer(4),
newPeer(6),
newPeer(8),
newPeer(10),
newPeer(11),
newPeer(12),
newPeer(13),
newPeer(14),
newPeer(15),
}.toMembers()
identities := identitySet(pkiID2MSPID)
for _, tst := range []struct {
name string
arguments *discoveryprotos.ChaincodeInterest
totalExistingMembers discovery.Members
metadata []*chaincode.Metadata
expected discovery.Members
}{
{
name: "Nil interest",
arguments: nil,
totalExistingMembers: members,
expected: members,
},
{
name: "Empty interest invocation chain",
arguments: &discoveryprotos.ChaincodeInterest{},
totalExistingMembers: members,
expected: members,
},
{
name: "Chaincodes only installed on some peers",
arguments: &discoveryprotos.ChaincodeInterest{
Chaincodes: []*discoveryprotos.ChaincodeCall{
{Name: cc1}, {Name: cc2},
},
},
totalExistingMembers: members2,
metadata: []*chaincode.Metadata{{
Name: "cc1", Version: "1.1",
}, {
Name: "cc2", Version: "1.0",
}},
expected: peerSet{newPeer(13).withChaincode(cc1, "1.1").withChaincode(cc2, "1.0")}.toMembers(),
},
{
name: "Only some peers authorized by collection",
arguments: &discoveryprotos.ChaincodeInterest{
Chaincodes: []*discoveryprotos.ChaincodeCall{
{Name: cc1, CollectionNames: []string{"collection"}},
},
},
totalExistingMembers: members,
metadata: []*chaincode.Metadata{{
Name: cc1, Version: "1.0",
CollectionsConfig: buildCollectionConfig(map[string][]*msp.MSPPrincipal{
"collection": {
peerRole("p0"),
peerRole("p12"),
},
}),
}},
expected: peerSet{
newPeer(0).withChaincode(cc1, "1.0"),
newPeer(12).withChaincode(cc1, "1.0")}.toMembers(),
},
} {
t.Run(tst.name, func(t *testing.T) {
g := &gossipMock{}
pf := &policyFetcherMock{}
mf := &metadataFetcher{}
g.On("Peers").Return(alivePeers)
g.On("IdentityInfo").Return(identities)
g.On("PeersOfChannel").Return(tst.totalExistingMembers).Once()
for _, md := range tst.metadata {
mf.On("Metadata").Return(md).Once()
}
analyzer := NewEndorsementAnalyzer(g, pf, &principalEvaluatorMock{}, mf)
actualMembers, err := analyzer.PeersAuthorizedByCriteria(common.ChainID("mychannel"), tst.arguments)
assert.NoError(t, err)
assert.Equal(t, tst.expected, actualMembers)
})
}
}

신뢰도에 따른 평가 가중치 알고리즘 참고 및 활용

2. Channel(채널)

peer 간의 통신은 채널을 통해서만 이루어집니다. 모든 조직이 채널을 통해 정보를 공유할 수도 있고 또는 비즈니스 이해관계가 맞는 일부 조직들 간에만 추가로 채널을 생성하여 정보를 공유할 수도 있습니다. 그리고 각 채널마다 하나씩 분산원장이 존재하게 되는데, 이더리움 등 모든 블록체인 구성원이 분산원장에 접근할 수 있는 퍼블릭 블록체인과 달리 하이퍼레저 패브릭에서는 채널에 참여한 조직의 구성원만이 채널의 분산원장에 접근할 수 있습니다. 즉, 기존의 퍼블릭 블록체인에서 제공할 수 없었던 데이터의 기밀성을 채널을 통해 제공할 수 있는 것입니다.

// InitCmdFactory init the ChannelCmdFactory with clients to endorser and orderer according to params
func InitCmdFactory(isEndorserRequired, isPeerDeliverRequired, isOrdererRequired bool) (*ChannelCmdFactory, error) {
if isPeerDeliverRequired && isOrdererRequired {
// this is likely a bug during development caused by adding a new cmd
return nil, errors.New("ERROR - only a single deliver source is currently supported")
}
var err error
cf := &ChannelCmdFactory{}
cf.Signer, err = common.GetDefaultSignerFnc()
if err != nil {
return nil, errors.WithMessage(err, "error getting default signer")
}
cf.BroadcastFactory = func() (common.BroadcastClient, error) {
return common.GetBroadcastClientFnc()
}
// for join and list, we need the endorser as well
if isEndorserRequired {
// creating an EndorserClient with these empty parameters will create a
// connection using the values of "peer.address" and
// "peer.tls.rootcert.file"
cf.EndorserClient, err = common.GetEndorserClientFnc(common.UndefinedParamValue, common.UndefinedParamValue)
if err != nil {
return nil, errors.WithMessage(err, "error getting endorser client for channel")
}
}
// for fetching blocks from a peer
if isPeerDeliverRequired {
cf.DeliverClient, err = common.NewDeliverClientForPeer(channelID, bestEffort)
if err != nil {
return nil, errors.WithMessage(err, "error getting deliver client for channel")
}
}
// for create and fetch, we need the orderer as well
if isOrdererRequired {
if len(strings.Split(common.OrderingEndpoint, ":")) != 2 {
return nil, errors.Errorf("ordering service endpoint %s is not valid or missing", common.OrderingEndpoint)
}
cf.DeliverClient, err = common.NewDeliverClientForOrderer(channelID, bestEffort)
if err != nil {
return nil, err
}
}
logger.Infof("Endorser and orderer connections initialized")
return cf, nil
}

스마트폰 내에서 관리하는 UTXO 정보의 지속적 용량 증가 문제 해결 참고

3. Gossip(가십)

  • peer는 끊임없이 브로드캐스트 메시지를 생성하여 동일한 채널에 있는 peer들의 상태를 확인 합니다. 만약 peer가 특정 시간 동안 브로드캐스트 메시지에 대한 응답을 하지 못한다면 어떻게 될까요? 브로드캐스트 메시지에 응답하지 못한 peer에 문제가 발생한 것으로 간주하고 해당 peer는 네트워크에서 오프라인 상태로 인식됩니다.

  • peer는 Gossip 프로토콜을 통해 같은 채널 내 peer들을 랜덤하게 선택하여 분산원장을 전송합니다. Gossip 프로토콜을 통해 분산원자을 수신한 peer는 자신의 분산원장과 비교하여 업데이트된 최신 정보가 있으면 자신의 분산원장에 해당 내용을 업데이트 합니다. 각 조직의 peer는 orderer로부터 분산원장을 업데이트할 수 는데, 조직의 모든 peer가 orderer에게 분산원장을 요청하면 orderer가 과부화될 가능성이 높아지게 됩니다. 그리하여 각 조직에서는 모든 peer가 orderer와 통신하는 대신 Leader peer를 대표로 선출하여 orderer와 통신을 하게 됩니다.

package gossip
import (
"fmt"
"time"
"github.com/hyperledger/fabric/gossip/api"
"github.com/hyperledger/fabric/gossip/comm"
"github.com/hyperledger/fabric/gossip/common"
"github.com/hyperledger/fabric/gossip/discovery"
"github.com/hyperledger/fabric/gossip/filter"
proto "github.com/hyperledger/fabric/protos/gossip"
)
// Gossip is the interface of the gossip component
type Gossip interface {
// SelfMembershipInfo returns the peer's membership information
SelfMembershipInfo() discovery.NetworkMember
// SelfChannelInfo returns the peer's latest StateInfo message of a given channel
SelfChannelInfo(common.ChainID) *proto.SignedGossipMessage
// Send sends a message to remote peers
Send(msg *proto.GossipMessage, peers ...*comm.RemotePeer)
// SendByCriteria sends a given message to all peers that match the given SendCriteria
SendByCriteria(*proto.SignedGossipMessage, SendCriteria) error
// GetPeers returns the NetworkMembers considered alive
Peers() []discovery.NetworkMember
// PeersOfChannel returns the NetworkMembers considered alive
// and also subscribed to the channel given
PeersOfChannel(common.ChainID) []discovery.NetworkMember
// UpdateMetadata updates the self metadata of the discovery layer
// the peer publishes to other peers
UpdateMetadata(metadata []byte)
// UpdateLedgerHeight updates the ledger height the peer
// publishes to other peers in the channel
UpdateLedgerHeight(height uint64, chainID common.ChainID)
// UpdateChaincodes updates the chaincodes the peer publishes
// to other peers in the channel
UpdateChaincodes(chaincode []*proto.Chaincode, chainID common.ChainID)
// Gossip sends a message to other peers to the network
Gossip(msg *proto.GossipMessage)
// PeerFilter receives a SubChannelSelectionCriteria and returns a RoutingFilter that selects
// only peer identities that match the given criteria, and that they published their channel participation
PeerFilter(channel common.ChainID, messagePredicate api.SubChannelSelectionCriteria) (filter.RoutingFilter, error)
// Accept returns a dedicated read-only channel for messages sent by other nodes that match a certain predicate.
// If passThrough is false, the messages are processed by the gossip layer beforehand.
// If passThrough is true, the gossip layer doesn't intervene and the messages
// can be used to send a reply back to the sender
Accept(acceptor common.MessageAcceptor, passThrough bool) (<-chan *proto.GossipMessage, <-chan proto.ReceivedMessage)
// JoinChan makes the Gossip instance join a channel
JoinChan(joinMsg api.JoinChannelMessage, chainID common.ChainID)
// LeaveChan makes the Gossip instance leave a channel.
// It still disseminates stateInfo message, but doesn't participate
// in block pulling anymore, and can't return anymore a list of peers
// in the channel.
LeaveChan(chainID common.ChainID)
// SuspectPeers makes the gossip instance validate identities of suspected peers, and close
// any connections to peers with identities that are found invalid
SuspectPeers(s api.PeerSuspector)
// IdentityInfo returns information known peer identities
IdentityInfo() api.PeerIdentitySet
// Stop stops the gossip component
Stop()
}
... 생략 ...

네트워크 레이어에서의 통신규약 참고 및 활용

Reference

  1. 하이퍼레저 패브릭으로 배우는 블록체인 / ETRI 블록체인기술연구센터 윤대근/ 제이펍