풋내기 학습Fabric 원본 학습 - Endorser 배서 노드
Fabric 1.4 소스 분석 Endorser 백서 노드
본 문서는 주로fabric 배서 노드의 주요 기능과 그 실현을 소개한다.
1. 소개
Endorser 노드는 peer 노드가 맡은 역할로 peer가 시작될 때 Endorser 백서 서버를 만들고 로컬 gRPC 서버(7051 포트)에 등록하여 대외적으로 서비스를 제공합니다. 요청한 서명 제안 메시지에 대해 시작 코드 용기, 아날로그 실행 코드 코드, 백서 서명 등 절차를 실행합니다.모든 클라이언트가 장부에 제출한 호출 거래는 백서 노드가 백서를 해야 한다. 클라이언트가 충분한 백서 정보를 수집한 후에 서명 제안 메시지, 시뮬레이션 실행 결과와 백서 정보를order 노드에 포장하여 블록을 정렬한다.
배서 작성자 Endorser는 거래 흐름에서 다음과 같은 역할을 합니다.
2. 백서 서버 초기화
peer 노드가 시작되면 백서 서버가 등록됩니다.
serverEndorser := endorser.NewEndorserServer(privDataDist, endorserSupport, pr, metricsProvider)
auth := authHandler.ChainFilters(serverEndorser, authFilters...)
// Register the Endorser server
pb.RegisterEndorserServer(peerServer.Server(), auth)
그 중에서 배서 서비스의 가장 중요한 인터페이스는
// EndorserServer is the server API for Endorser service.
type EndorserServer interface {
ProcessProposal(context.Context, *SignedProposal) (*ProposalResponse, error)
}
ProcessProposal () 서비스 인터페이스의 주요 기능은 서명 제안 메시지 수신 및 처리(SignedProposal), 코드 용기 시작, 호출 코드 실행 및 서명 배서입니다.
3. 책 외우기 서비스
ProcessProposal() 서비스에는 다음과 같은 프로세스가 주로 존재합니다.
소스 코드는 다음과 같습니다.
func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal) (*pb.ProposalResponse, error) {
...
// 0 -- check and validate
vr, err := e.preProcess(signedProp)
if err != nil {
resp := vr.resp
return resp, err
}
prop, hdrExt, chainID, txid := vr.prop, vr.hdrExt, vr.chainID, vr.txid
txParams := &ccprovider.TransactionParams{
ChannelID: chainID,
TxID: txid,
SignedProp: signedProp,
Proposal: prop,
TXSimulator: txsim,
HistoryQueryExecutor: historyQueryExecutor,
}
// 1 -- simulate
cd, res, simulationResult, ccevent, err := e.SimulateProposal(txParams, hdrExt.ChaincodeId)
if err != nil {
return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, nil
}
...
// 2 -- endorse and get a marshalled ProposalResponse message
var pResp *pb.ProposalResponse
if chainID == "" {
pResp = &pb.ProposalResponse{Response: res}
} else {
pResp, err = e.endorseProposal(ctx, chainID, txid, signedProp, prop, res, simulationResult, ccevent, hdrExt.PayloadVisibility, hdrExt.ChaincodeId, txsim, cd)
...
}
pResp.Response = res
return pResp, nil
}
3.1 서명 제안의 합법성 검토 및 검증
preProcess () 방법은 서명 제안 메시지를 미리 처리합니다. 주로 메시지 형식과 서명의 합법성을 검증하고 제안 메시지가 대응하는 체인 코드가 시스템 체인 코드인지 외부 호출되지 않는지, 거래의 유일성, 대응 채널의 접근 제어 전략을 충족시키는지 검증합니다.
func (e *Endorser) preProcess(signedProp *pb.SignedProposal) (*validateResult, error) {
vr := &validateResult{}
// 1.
prop, hdr, hdrExt, err := validation.ValidateProposalMessage(signedProp)
chdr, err := putils.UnmarshalChannelHeader(hdr.ChannelHeader)
shdr, err := putils.GetSignatureHeader(hdr.SignatureHeader)
// block invocations to security-sensitive system chaincodes
// 2.
if e.s.IsSysCCAndNotInvokableExternal(hdrExt.ChaincodeId.Name) {
vr.resp = &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}
return vr, err
}
chainID := chdr.ChannelId
txid := chdr.TxId
if chainID != "" {
// 3.
if _, err = e.s.GetTransactionByID(chainID, txid); err == nil {
err = errors.Errorf("duplicate transaction found [%s]. Creator [%x]", txid, shdr.Creator)
vr.resp = &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}
return vr, err
}
if !e.s.IsSysCC(hdrExt.ChaincodeId.Name) {
// check that the proposal complies with the Channel's writers
// 4. acl
if err = e.s.CheckACL(signedProp, chdr, shdr, hdrExt); err != nil {
e.Metrics.ProposalACLCheckFailed.With(meterLabels...).Add(1)
return vr, err
}
}
} else {
}
vr.prop, vr.hdrExt, vr.chainID, vr.txid = prop, hdrExt, chainID, txid
return vr, nil
}
3.1.1 메시지 형식과 서명의 합법성 검증
preProcess()에서 ValidateProposalMessage()를 호출하여 메시지를 검증합니다.주로 메시지의 형식, 서명, 거래 id를 검증합니다.
먼저 validateCommonHeader() 검증 Proposal을 호출합니다.Header의 합법성.
// checks for a valid Header
func validateCommonHeader(hdr *common.Header) (*common.ChannelHeader, *common.SignatureHeader, error) {
if hdr == nil {
return nil, nil, errors.New("nil header")
}
chdr, err := utils.UnmarshalChannelHeader(hdr.ChannelHeader)
shdr, err := utils.GetSignatureHeader(hdr.SignatureHeader)
// HeaderType_ENDORSER_TRANSACTION、HeaderType_CONFIG_UPDATE、HeaderType_CONFIG、HeaderType_TOKEN_TRANSACTION, Epoch 0
err = validateChannelHeader(chdr)
// shdr shdr.Nonce shdr.Creator nil, 0
err = validateSignatureHeader(shdr)
return chdr, shdr, nil
}
이어서 checkSignatureFromCreator()를 호출하여 서명을 검증합니다.그 중에서 우선 전송된 매개 변수가nil인지 확인한 다음creator.Validate () 는 작성자 creator를 검증합니다.
err = checkSignatureFromCreator(shdr.Creator, signedProp.Signature, signedProp.ProposalBytes, chdr.ChannelId)
그리고 거래 id를 검증하여 거래 id가 계산된 거래 id와 일치하는지 검증합니다
err = utils.CheckTxID(
chdr.TxId,
shdr.Nonce,
shdr.Creator)
// id
func ComputeTxID(nonce, creator []byte) (string, error) {
digest, err := factory.GetDefault().Hash(
append(nonce, creator...),
&bccsp.SHA256Opts{})
if err != nil {
return "", err
}
return hex.EncodeToString(digest), nil
}
마지막으로 메시지 유형에 따라 분류 처리:
switch common.HeaderType(chdr.Type) {
case common.HeaderType_CONFIG:
fallthrough
case common.HeaderType_ENDORSER_TRANSACTION:
chaincodeHdrExt, err := validateChaincodeProposalMessage(prop, hdr)
if err != nil {
return nil, nil, nil, err
}
return prop, hdr, chaincodeHdrExt, err
default:
return nil, nil, nil, errors.Errorf("unsupported proposal type %d", common.HeaderType(chdr.Type))
}
그 중에서 validateChaincodeProposalMessage() 방법은 입력 매개 변수가 nil이 아니라는 것을 검증하고, GetChaincodeHeaderExtension() 방법을 호출하여 chaincodeHdrext를 가져오고, chaincodeHdrext를 검증합니다.ChaincodeId가 nil인지 여부와 chaincodeHdrExt.PayloadVisibility가 nil이 아닌지(현재는 nil)
3.1.2 시스템 체인 코드인지 확인하고 외부에 호출되지 않음
preProcess() 호출 IsSysCCAndNotInvokableExternal () 방법 검증 제안 메시지 헤더 hdrext.ChaincodeId.Name 체인 이름에 해당하는 체인 코드가 외부 호출을 허용하는 시스템 체인 코드인지 여부입니다.모든 시스템 체인 코드(lscc,vscc,escc,qscc,cscc)를 훑어보았는데, 그 중에서 vscc,escc는 외부 호출이 아니다
func (p *Provider) IsSysCCAndNotInvokableExternal(name string) bool {
for _, sysCC := range p.SysCCs {
if sysCC.Name() == name {
return !sysCC.InvokableExternal()
}
}
if isDeprecatedSysCC(name) {
return true
}
return false
}
func isDeprecatedSysCC(name string) bool {
return name == "vscc" || name == "escc"
}
3.1.3 서명 제안 메시지 거래 id의 유일성 검사
먼저 이 장부가 존재하는지 확인한 다음에 장부에 거래 id가 존재하는지 확인합니다.
func (s *SupportImpl) GetTransactionByID(chid, txID string) (*pb.ProcessedTransaction, error) {
lgr := s.Peer.GetLedger(chid)
if lgr == nil {
return nil, errors.Errorf("failed to look up the ledger for Channel %s", chid)
}
tx, err := lgr.GetTransactionByID(txID)
if err != nil {
return nil, errors.WithMessage(err, "GetTransactionByID failed")
}
return tx, nil
}
3.1.4 해당 채널에 대한 액세스 제어 정책 충족 여부 확인
배서 노드는 배서 과정에서 응용 채널의 Writers 정책을 충족하는지 확인합니다.CheckACL () 메서드는 마지막으로 core/endorser/support를 호출합니다.go CheckACL()
func (s *SupportImpl) CheckACL(signedProp *pb.SignedProposal, chdr *common.ChannelHeader, shdr *common.SignatureHeader, hdrext *pb.ChaincodeHeaderExtension) error {
return s.ACLProvider.CheckACL(resources.Peer_Propose, chdr.ChannelId, signedProp)
}
다음을 수행합니다.
Peer_Propose = "peer/Propose"
d.cResourcePolicyMap[resources.Peer_Propose] = CHANNELWRITERS
서명 제안 메시지 형식에 따라 코어/aclmgmt/defaultaclprovider를 호출합니다.go CheckACL() 메서드
case *pb.SignedProposal:
return d.policyChecker.CheckPolicy(channelID, policy, typedData)
최종적으로 호출될 것이다
func (p *policyChecker) CheckPolicyBySignedData(channelID, policyName string, sd []*common.SignedData) error {
if channelID == "" {
return errors.New("Invalid channel ID name during check policy on signed data. Name must be different from nil.")
}
if policyName == "" {
return fmt.Errorf("Invalid policy name during check policy on signed data on channel [%s]. Name must be different from nil.", channelID)
}
if sd == nil {
return fmt.Errorf("Invalid signed data during check policy on channel [%s] with policy [%s]", channelID, policyName)
}
// Get Policy
policyManager, _ := p.channelPolicyManagerGetter.Manager(channelID)
if policyManager == nil {
return fmt.Errorf("Failed to get policy manager for channel [%s]", channelID)
}
// Recall that get policy always returns a policy object
policy, _ := policyManager.GetPolicy(policyName)
// Evaluate the policy
err := policy.Evaluate(sd)
if err != nil {
return fmt.Errorf("Failed evaluating policy on signed data during check policy on channel [%s] with policy [%s]: [%s]", channelID, policyName, err)
}
return nil
}
다음을 수행합니다.
sd := []*common.SignedData{{
Data: signedProp.ProposalBytes,
Identity: shdr.Creator,
Signature: signedProp.Signature,
}}
3.2 체인 코드 호출 및 시뮬레이션 실행 제안
우선, Process Proposal () 방법 호출 방법 acquire Tx Simulator () 는 체인 코드에 따라 거래 시뮬레이터 Tx Simulator를 만들어야 하는지 판단하고, 필요하면 거래 시뮬레이터 Tx Simulator (역사 기록을 조회할 수 없음) 와 역사 기록 조회기 HistoryQuery Executor를 만들고, 이어서 Simulate Proposal () 시뮬레이션 거래 제안 메시지를 호출하여 시뮬레이션 실행 결과를 되돌려줍니다.그 중에서 체인 코드 qscc, cscc는 거래 시뮬레이터를 필요로 하지 않습니다.
unc acquireTxSimulator(chainID string, ccid *pb.ChaincodeID) bool {
if chainID == "" {
return false
}
// ¯\_(ツ)_/¯ locking.
// Don't get a simulator for the query and config system chaincode.
// These don't need the simulator and its read lock results in deadlocks.
switch ccid.Name {
case "qscc", "cscc":
return false
default:
return true
}
}
SimulateProposal() 방법에서 먼저 호출 체인 코드가 시스템 체인 코드인지 여부를 판단합니다.
if simResult, err = txParams.TXSimulator.GetTxSimulationResults(); err != nil {
txParams.TXSimulator.Done()
return nil, nil, nil, nil, err
}
if err := e.distributePrivateData(txParams.ChannelID, txParams.TxID, pvtDataWithConfig, endorsedAt); err != nil {
return nil, nil, nil, nil, err
}
if pubSimResBytes, err = simResult.GetPubSimulationBytes(); err != nil {
return nil, nil, nil, nil, err
}
3.2.1 실례화 정책 검토
CheckInstantiationPolicy () 는 GetChaincodeData () 를 호출하여 캐시나 로컬 파일 시스템에서 설치된 체인 패키지 CCPackage를 가져와서 ChaincodeData 대상 ccdata로 해석합니다.장부에 저장된 체인 코드에 대한 실례화 전략과 비교한다.
func CheckInstantiationPolicy(name, version string, cdLedger *ChaincodeData) error {
ccdata, err := GetChaincodeData(name, version)
if err != nil {
return err
}
if ccdata.InstantiationPolicy != nil {
if !bytes.Equal(ccdata.InstantiationPolicy, cdLedger.InstantiationPolicy) {
return fmt.Errorf("Instantiation policy mismatch for cc %s/%s", name, version)
}
}
return nil
}
실례화 정책은 체인 코드를 설치할 때 지정합니다
3.2.2 호출 체인 코드
SimulateProposal () 메서드에서 callChaincode () 메서드가 체인 코드를 호출합니다.먼저 Execute () 방법을 실행하여 체인 코드를 호출하고 "deploy"와 "upgrade"동작을 처리합니다.
3.2.2.1 Execute () 작업
SimulateProposal () 메서드는 Execute () 실행 체인 코드를 호출하여 최종적으로 코어/chaincode/chaincode_support.go Execute () 메서드
func (cs *ChaincodeSupport) Execute(txParams *ccprovider.TransactionParams, cccid *ccprovider.CCContext, input *pb.ChaincodeInput) (*pb.Response, *pb.ChaincodeEvent, error) {
resp, err := cs.Invoke(txParams, cccid, input)
return processChaincodeExecutionResult(txParams.TxID, cccid.Name, resp, err)
}
func (cs *ChaincodeSupport) Invoke(txParams *ccprovider.TransactionParams, cccid *ccprovider.CCContext, input *pb.ChaincodeInput) (*pb.ChaincodeMessage, error) {
h, err := cs.Launch(txParams.ChannelID, cccid.Name, cccid.Version, txParams.TXSimulator)
if err != nil {
return nil, err
}
cctype := pb.ChaincodeMessage_TRANSACTION
return cs.execute(cctype, txParams, cccid, input, h)
}
== 메시지 유형: ChaincodeMessage_TRANSACTION==, execute () 방법도 호출되었습니다.
func processChaincodeExecutionResult(txid, ccName string, resp *pb.ChaincodeMessage, err error) (*pb.Response, *pb.ChaincodeEvent, error) {
...
if resp.ChaincodeEvent != nil {
resp.ChaincodeEvent.ChaincodeId = ccName
resp.ChaincodeEvent.TxId = txid
}
switch resp.Type {
case pb.ChaincodeMessage_COMPLETED:
res := &pb.Response{}
err := proto.Unmarshal(resp.Payload, res)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to unmarshal response for transaction %s", txid)
}
return res, resp.ChaincodeEvent, nil
case pb.ChaincodeMessage_ERROR:
return nil, resp.ChaincodeEvent, errors.Errorf("transaction returned with failure: %s", resp.Payload)
default:
return nil, nil, errors.Errorf("unexpected response type %d for transaction %s", resp.Type, txid)
}
}
3.2.2.2 "deploy"/"upgrade"작업
주요 구현 방법은 ExecuteLegacyInit()이며 이 프로세스는 Execute()와 유사합니다.
func (cs *ChaincodeSupport) ExecuteLegacyInit(txParams *ccprovider.TransactionParams, cccid *ccprovider.CCContext, spec *pb.ChaincodeDeploymentSpec) (*pb.Response, *pb.ChaincodeEvent, error) {
ccci := ccprovider.DeploymentSpecToChaincodeContainerInfo(spec)
ccci.Version = cccid.Version
err := cs.LaunchInit(ccci)
if err != nil {
return nil, nil, err
}
cname := ccci.Name + ":" + ccci.Version
h := cs.HandlerRegistry.Handler(cname)
if h == nil {
return nil, nil, errors.Wrapf(err, "[channel %s] claimed to start chaincode container for %s but could not find handler", txParams.ChannelID, cname)
}
resp, err := cs.execute(pb.ChaincodeMessage_INIT, txParams, cccid, spec.GetChaincodeSpec().Input, h)
return processChaincodeExecutionResult(txParams.TxID, cccid.Name, resp, err)
}
여기서 == 메시지 유형은 ChaincodeMessage_INIT==
3.2.3 시뮬레이션 실행 결과 처리
시뮬레이션 실행 결과에 대해 처리하다.체인 코드 시뮬레이션을 실행한 후 시뮬레이션 실행 결과를 거래 시뮬레이터 TXSimulator에 기록합니다.GetTxSimulationResults() 메서드를 호출하여 시뮬레이션 실행 결과를 얻을 수 있습니다.TxSimulationResults는 공유 데이터 읽기 세트PubSimulationResults와 개인 데이터 읽기 세트 PvtSimulationResults를 포함합니다.
3.2.3.1 시뮬레이션 실행 결과 얻기
SimulateProposal() 메서드는 GetTxSimulationResults() 메서드를 호출하여 시뮬레이션 실행 결과를 가져옵니다.원본은 아래와 같다.
func (b *RWSetBuilder) GetTxSimulationResults() (*ledger.TxSimulationResults, error) {
//
pvtData := b.getTxPvtReadWriteSet()
var err error
var pubDataProto *rwset.TxReadWriteSet
var pvtDataProto *rwset.TxPvtReadWriteSet
// Populate the collection-level hashes into pub rwset and compute the proto bytes for pvt rwset
// hash
if pvtData != nil {
if pvtDataProto, err = pvtData.toProtoMsg(); err != nil {
return nil, err
}
// hash
for _, ns := range pvtDataProto.NsPvtRwset {
for _, coll := range ns.CollectionPvtRwset {
// hash
b.setPvtCollectionHash(ns.Namespace, coll.CollectionName, coll.Rwset)
}
}
}
// Compute the proto bytes for pub rwset
//
pubSet := b.GetTxReadWriteSet()
if pubSet != nil {
if pubDataProto, err = b.GetTxReadWriteSet().toProtoMsg(); err != nil {
return nil, err
}
}
//
return &ledger.TxSimulationResults{
PubSimulationResults: pubDataProto,
PvtSimulationResults: pvtDataProto,
}, nil
}
3.2.3.2 데이터 처리
func (g *gossipServiceImpl) DistributePrivateData(chainID string, txID string, privData *transientstore.TxPvtReadWriteSetWithConfigInfo, blkHt uint64) error {
g.lock.RLock()
handler, exists := g.privateHandlers[chainID]
g.lock.RUnlock()
if !exists {
return errors.Errorf("No private data handler for %s", chainID)
}
if err := handler.distributor.Distribute(txID, privData, blkHt); err != nil {
logger.Error("Failed to distributed private collection, txID", txID, "channel", chainID, "due to", err)
return err
}
if err := handler.coordinator.StorePvtData(txID, privData, blkHt); err != nil {
logger.Error("Failed to store private data into transient store, txID",
txID, "channel", chainID, "due to", err)
return err
}
return nil
}
if pubSimResBytes, err = simResult.GetPubSimulationBytes(); err != nil {
return nil, nil, nil, nil, err
}
func (txSim *TxSimulationResults) GetPubSimulationBytes() ([]byte, error) {
return proto.Marshal(txSim.PubSimulationResults)
}
3.3 서명 배서
ProcessProposal () 방법에서 먼저 채널 id가 nil인지 아닌지를 판단하고, nil이면 응답 결과를 직접 되돌려줍니다. (예를 들어 install 작업)nil이 아니라면endorseProposal () 방법으로 시뮬레이션 실행 결과에 서명하고 책을 외웁니다.endorse Proposal () 방법에서 Context 대상을 구성하고 Endorse With Plugin () 에서 getorCreate Plugin () 을 호출하여 플러그인을 만들고 proposal Response Payload From Context () 방법을 호출합니다. 이 방법에서 백서 결과hash 및 봉인 시뮬레이션 실행 결과, 체인 이벤트 및 체인 응답 결과 등(데이터 구조는 Proposal Response Payload)을 계산하여 []byte 그룹으로 서열화합니다.마지막으로 Endorse () 방법을 호출하여 서명 배서 작업을 실행합니다. (escc는 현재 플러그인 형식으로 실행되기 때문에 안에서 판단합니다. 기본적으로escc를 실행합니다.
func (e *DefaultEndorsement) Endorse(prpBytes []byte, sp *peer.SignedProposal) (*peer.Endorsement, []byte, error) {
signer, err := e.SigningIdentityForRequest(sp)
if err != nil {
return nil, nil, errors.Wrap(err, "failed fetching signing identity")
}
// serialize the signing identity
identityBytes, err := signer.Serialize()
if err != nil {
return nil, nil, errors.Wrapf(err, "could not serialize the signing identity")
}
// sign the concatenation of the proposal response and the serialized endorser identity with this endorser's key
signature, err := signer.Sign(append(prpBytes, identityBytes...))
if err != nil {
return nil, nil, errors.Wrapf(err, "could not sign the proposal response payload")
}
endorsement := &peer.Endorsement{Signature: signature, Endorser: identityBytes}
return endorsement, prpBytes, nil
}
이 내용에 흥미가 있습니까?
현재 기사가 여러분의 문제를 해결하지 못하는 경우 AI 엔진은 머신러닝 분석(스마트 모델이 방금 만들어져 부정확한 경우가 있을 수 있음)을 통해 가장 유사한 기사를 추천합니다:
다양한 언어의 JSONJSON은 Javascript 표기법을 사용하여 데이터 구조를 레이아웃하는 데이터 형식입니다. 그러나 Javascript가 코드에서 이러한 구조를 나타낼 수 있는 유일한 언어는 아닙니다. 저는 일반적으로 '객체'{}...
텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
CC BY-SA 2.5, CC BY-SA 3.0 및 CC BY-SA 4.0에 따라 라이센스가 부여됩니다.