fabric1.4 체인 코드 실례화 원본 분석

16766 단어

체인 코드 컨테이너 시작


peer 노드 배서 제안의 입구 방법endorser.goProcessProposal() 방법
func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal) (*pb.ProposalResponse, error) {
    // start time for computing elapsed time metric for successfully endorsed proposals
    //  peer 
    startTime := time.Now()
    // Peer +1
    e.Metrics.ProposalsReceived.Add(1)

    //  
    addr := util.ExtractRemoteAddress(ctx)
    endorserLogger.Debug("Entering: request from", addr)

제안 예처리


제안에 대한 시뮬레이션 실행

endorser.goSimulateProposal() 방법
func (e *Endorser) SimulateProposal(txParams *ccprovider.TransactionParams, cid *pb.ChaincodeID) (ccprovider.ChaincodeDefinition, *pb.Response, []byte, *pb.ChaincodeEvent, error) {
    endorserLogger.Debugf("[%s][%s] Entry chaincode: %s", txParams.ChannelID, shorttxid(txParams.TxID), cid)
    defer endorserLogger.Debugf("[%s][%s] Exit", txParams.ChannelID, shorttxid(txParams.TxID))
    // we do expect the payload to be a ChaincodeInvocationSpec
    // if we are supporting other payloads in future, this be glaringly point
    // as something that should change
    cis, err := putils.GetChaincodeInvocationSpec(txParams.Proposal)
    if err != nil {
        return nil, nil, nil, nil, err
    }

    var cdLedger ccprovider.ChaincodeDefinition
    var version string

    if !e.s.IsSysCC(cid.Name) {
        cdLedger, err = e.s.GetChaincodeDefinition(cid.Name, txParams.TXSimulator)
        if err != nil {
            return nil, nil, nil, nil, errors.WithMessage(err, fmt.Sprintf("make sure the chaincode %s has been successfully instantiated and try again", cid.Name))
        }
        version = cdLedger.CCVersion()

        err = e.s.CheckInstantiationPolicy(cid.Name, version, cdLedger)
        if err != nil {
            return nil, nil, nil, nil, err
        }
    } else {
        version = util.GetSysCCVersion()
    }

    // ---3. execute the proposal and get simulation results
    var simResult *ledger.TxSimulationResults
    var pubSimResBytes []byte
    var res *pb.Response
    var ccevent *pb.ChaincodeEvent
    res, ccevent, err = e.callChaincode(txParams, version, cis.ChaincodeSpec.Input, cid)
    if err != nil {
        endorserLogger.Errorf("[%s][%s] failed to invoke chaincode %s, error: %+v", txParams.ChannelID, shorttxid(txParams.TxID), cid, err)
        return nil, nil, nil, nil, err
    }

    if txParams.TXSimulator != nil {
        if simResult, err = txParams.TXSimulator.GetTxSimulationResults(); err != nil {
            txParams.TXSimulator.Done()
            return nil, nil, nil, nil, err
        }

        if simResult.PvtSimulationResults != nil {
            if cid.Name == "lscc" {
                // TODO: remove once we can store collection configuration outside of LSCC
                txParams.TXSimulator.Done()
                return nil, nil, nil, nil, errors.New("Private data is forbidden to be used in instantiate")
            }
            pvtDataWithConfig, err := e.AssemblePvtRWSet(simResult.PvtSimulationResults, txParams.TXSimulator)
            // To read collection config need to read collection updates before
            // releasing the lock, hence txParams.TXSimulator.Done()  moved down here
            txParams.TXSimulator.Done()

            if err != nil {
                return nil, nil, nil, nil, errors.WithMessage(err, "failed to obtain collections config")
            }
            endorsedAt, err := e.s.GetLedgerHeight(txParams.ChannelID)
            if err != nil {
                return nil, nil, nil, nil, errors.WithMessage(err, fmt.Sprint("failed to obtain ledger height for channel", txParams.ChannelID))
            }
            // Add ledger height at which transaction was endorsed,
            // `endorsedAt` is obtained from the block storage and at times this could be 'endorsement Height + 1'.
            // However, since we use this height only to select the configuration (3rd parameter in distributePrivateData) and
            // manage transient store purge for orphaned private writesets (4th parameter in distributePrivateData), this works for now.
            // Ideally, ledger should add support in the simulator as a first class function `GetHeight()`.
            pvtDataWithConfig.EndorsedAt = endorsedAt
            if err := e.distributePrivateData(txParams.ChannelID, txParams.TxID, pvtDataWithConfig, endorsedAt); err != nil {
                return nil, nil, nil, nil, err
            }
        }

        txParams.TXSimulator.Done()
        if pubSimResBytes, err = simResult.GetPubSimulationBytes(); err != nil {
            return nil, nil, nil, nil, err
        }
    }
    return cdLedger, res, pubSimResBytes, ccevent, nil
}
callChaincode() 방법 시뮬레이션 제안 집행
func (e *Endorser) callChaincode(txParams *ccprovider.TransactionParams, version string, input *pb.ChaincodeInput, cid *pb.ChaincodeID) (*pb.Response, *pb.ChaincodeEvent, error) {
    endorserLogger.Infof("[%s][%s] Entry chaincode: %s", txParams.ChannelID, shorttxid(txParams.TxID), cid)
    defer func(start time.Time) {
        logger := endorserLogger.WithOptions(zap.AddCallerSkip(1))
        elapsedMilliseconds := time.Since(start).Round(time.Millisecond) / time.Millisecond
        logger.Infof("[%s][%s] Exit chaincode: %s (%dms)", txParams.ChannelID, shorttxid(txParams.TxID), cid, elapsedMilliseconds)
    }(time.Now())

    var err error
    var res *pb.Response
    var ccevent *pb.ChaincodeEvent

    // is this a system chaincode
    res, ccevent, err = e.s.Execute(txParams, txParams.ChannelID, cid.Name, version, txParams.TxID, txParams.SignedProp, txParams.Proposal, input)
    if err != nil {
        return nil, nil, err
    }

    // per doc anything < 400 can be sent as TX.
    // fabric errors will always be >= 400 (ie, unambiguous errors )
    // "lscc" will respond with status 200 or 500 (ie, unambiguous OK or ERROR)
    if res.Status >= shim.ERRORTHRESHOLD {
        return res, nil, nil
    }

    // ----- BEGIN -  SECTION THAT MAY NEED TO BE DONE IN LSCC ------
    // if this a call to deploy a chaincode, We need a mechanism
    // to pass TxSimulator into LSCC. Till that is worked out this
    // special code does the actual deploy, upgrade here so as to collect
    // all state under one TxSimulator
    //
    // NOTE that if there's an error all simulation, including the chaincode
    // table changes in lscc will be thrown away
    if cid.Name == "lscc" && len(input.Args) >= 3 && (string(input.Args[0]) == "deploy" || string(input.Args[0]) == "upgrade") {
        userCDS, err := putils.GetChaincodeDeploymentSpec(input.Args[2], e.PlatformRegistry)
        if err != nil {
            return nil, nil, err
        }

        var cds *pb.ChaincodeDeploymentSpec
        cds, err = e.SanitizeUserCDS(userCDS)
        if err != nil {
            return nil, nil, err
        }

        // this should not be a system chaincode
        if e.s.IsSysCC(cds.ChaincodeSpec.ChaincodeId.Name) {
            return nil, nil, errors.Errorf("attempting to deploy a system chaincode %s/%s", cds.ChaincodeSpec.ChaincodeId.Name, txParams.ChannelID)
        }

        _, _, err = e.s.ExecuteLegacyInit(txParams, txParams.ChannelID, cds.ChaincodeSpec.ChaincodeId.Name, cds.ChaincodeSpec.ChaincodeId.Version, txParams.TxID, txParams.SignedProp, txParams.Proposal, cds)
        if err != nil {
            // increment the failure to indicate instantion/upgrade failures
            meterLabels := []string{
                "channel", txParams.ChannelID,
                "chaincode", cds.ChaincodeSpec.ChaincodeId.Name + ":" + cds.ChaincodeSpec.ChaincodeId.Version,
            }
            e.Metrics.InitFailed.With(meterLabels...).Add(1)
            return nil, nil, err
        }
    }
    // ----- END -------

    return res, ccevent, err
}

그중 e.s.Execute 실제 호출 support.goSupportImplExecute() 방법
func (s *SupportImpl) Execute(txParams *ccprovider.TransactionParams, cid, name, version, txid string, signedProp *pb.SignedProposal, prop *pb.Proposal, input *pb.ChaincodeInput) (*pb.Response, *pb.ChaincodeEvent, error) {
    cccid := &ccprovider.CCContext{
        Name:    name,
        Version: version,
    }

    // decorate the chaincode input
    decorators := library.InitRegistry(library.Config{}).Lookup(library.Decoration).([]decoration.Decorator)
    input.Decorations = make(map[string][]byte)
    input = decoration.Apply(prop, input, decorators...)
    txParams.ProposalDecorations = input.Decorations

    return s.ChaincodeSupport.Execute(txParams, cccid, input)
}

체인 코드 컨테이너 시작

ChaincodeSupport.Invoke 방법 중
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
    }

    // TODO add Init exactly once semantics here once new lifecycle
    // is available.  Enforced if the target channel is using the new lifecycle
    //
    // First, the function name of the chaincode to invoke should be checked.  If it is
    // "init", then consider this invocation to be of type pb.ChaincodeMessage_INIT,
    // otherwise consider it to be of type pb.ChaincodeMessage_TRANSACTION,
    //
    // Secondly, A check should be made whether the chaincode has been
    // inited, then, if true, only allow cctyp pb.ChaincodeMessage_TRANSACTION,
    // otherwise, only allow cctype pb.ChaincodeMessage_INIT,
    cctype := pb.ChaincodeMessage_TRANSACTION

    return cs.execute(cctype, txParams, cccid, input, h)
}

최종적으로 파일dockercontroller.go을 따라가서 이 파일에서 docker 용기 생성을 완성합니다

체인 코드 렌즈 생성 및 용기 생성

dockercontroller.go 에서
//  - 
func (vm *DockerVM) Start(ccid ccintf.CCID, args, env []string, filesToUpload map[string][]byte, builder container.Builder) error {
    imageName, err := vm.GetVMNameForDocker(ccid)
    if err != nil {
        return err
    }

    attachStdout := viper.GetBool("vm.docker.attachStdout")
    containerName := vm.GetVMName(ccid)
    logger := dockerLogger.With("imageName", imageName, "containerName", containerName)

    client, err := vm.getClientFnc()
    if err != nil {
        logger.Debugf("failed to get docker client", "error", err)
        return err
    }
    
    vm.stopInternal(client, containerName, 0, false, false)
    //  
    err = vm.createContainer(client, imageName, containerName, args, env, attachStdout)
    if err == docker.ErrNoSuchImage {
        //  , 
        //  reader DockerFile    
        reader, err := builder.Build()
        if err != nil {
            return errors.Wrapf(err, "failed to generate Dockerfile to build %s", containerName)
        }

        err = vm.deployImage(client, ccid, reader)
        if err != nil {
            return err
        }

그중builder.Build()은 생산Dockerfile을 하는 곳이고, builder의 실현은 PlatformBuilder
func (b *PlatformBuilder) Build() (io.Reader, error) {
    return b.PlatformRegistry.GenerateDockerBuild(
        b.Type,
        b.Path,
        b.Name,
        b.Version,
        b.CodePackage,
    )
}

진입PlatformRegistry 방법GenerateDockerBuild
func (r *Registry) GenerateDockerBuild(ccType, path, name, version string, codePackage []byte) (io.Reader, error) {

    inputFiles := make(map[string][]byte)

    // Generate the Dockerfile specific to our context
    //  dockerfile
    dockerFile, err := r.GenerateDockerfile(ccType, name, version)
    if err != nil {
        return nil, fmt.Errorf("Failed to generate a Dockerfile: %s", err)
    }

    inputFiles["Dockerfile"] = []byte(dockerFile)

    // ----------------------------------------------------------------------------------------------------
    // Finally, launch an asynchronous process to stream all of the above into a docker build context
    // ----------------------------------------------------------------------------------------------------
    input, output := io.Pipe()

    go func() {
        gw := gzip.NewWriter(output)
        tw := tar.NewWriter(gw)
        err := r.StreamDockerBuild(ccType, path, codePackage, inputFiles, tw)
        if err != nil {
            logger.Error(err)
        }

        tw.Close()
        gw.Close()
        output.CloseWithError(err)
    }()

    return input, nil
}

진입 방법GenerateDockerfile
func (r *Registry) GenerateDockerfile(ccType, name, version string) (string, error) {
    platform, ok := r.Platforms[ccType]
    if !ok {
        return "", fmt.Errorf("Unknown chaincodeType: %s", ccType)
    }

    var buf []string

    // ----------------------------------------------------------------------------------------------------
    // Let the platform define the base Dockerfile
    // ----------------------------------------------------------------------------------------------------
    //  dockerfile
    base, err := platform.GenerateDockerfile()
    if err != nil {
        return "", fmt.Errorf("Failed to generate platform-specific Dockerfile: %s", err)
    }
    buf = append(buf, base)

    // ----------------------------------------------------------------------------------------------------
    // Add some handy labels
    // ----------------------------------------------------------------------------------------------------
    buf = append(buf, fmt.Sprintf(`LABEL %s.chaincode.id.name="%s" \`, metadata.BaseDockerLabel, name))
    buf = append(buf, fmt.Sprintf(`      %s.chaincode.id.version="%s" \`, metadata.BaseDockerLabel, version))
    buf = append(buf, fmt.Sprintf(`      %s.chaincode.type="%s" \`, metadata.BaseDockerLabel, ccType))
    buf = append(buf, fmt.Sprintf(`      %s.version="%s" \`, metadata.BaseDockerLabel, metadata.Version))
    buf = append(buf, fmt.Sprintf(`      %s.base.version="%s"`, metadata.BaseDockerLabel, metadata.BaseVersion))
    // ----------------------------------------------------------------------------------------------------
    // Then augment it with any general options
    // ----------------------------------------------------------------------------------------------------
    //append version so chaincode build version can be compared against peer build version
    buf = append(buf, fmt.Sprintf("ENV CORE_CHAINCODE_BUILDLEVEL=%s", metadata.Version))

    // ----------------------------------------------------------------------------------------------------
    // Finalize it
    // ----------------------------------------------------------------------------------------------------
    contents := strings.Join(buf, "
") logger.Debugf("
%s", contents) return contents, nil

우리는 주로 JAVA 체인 코드 플랫폼core/chaincode/platforms/java/platform.goGenerateDockerfile 방법을 보았다
func (javaPlatform *Platform) GenerateDockerfile() (string, error) {
    var buf []string

    buf = append(buf, "FROM "+cutil.GetDockerfileFromConfig("chaincode.java.runtime"))
    buf = append(buf, "ADD binpackage.tar /root/chaincode-java/chaincode")

    dockerFileContents := strings.Join(buf, "
") return dockerFileContents, nil }

위의 코드에서 볼 수 있듯이 자바 체인 코드 용기에 의존하는 거울은 설정chaincode.java.runtime에서 읽고, 이 설정은 integration/nwo/core_template.go에서 읽습니다(아래는 일부 설정일 뿐입니다)
chaincode:
  builder: $(DOCKER_NS)/fabric-ccenv:$(ARCH)-$(PROJECT_VERSION)
  pull: false
  golang:
    runtime: $(BASE_DOCKER_NS)/fabric-baseos:$(ARCH)-$(BASE_VERSION)
    dynamicLink: false
  car:
    runtime: $(BASE_DOCKER_NS)/fabric-baseos:$(ARCH)-$(BASE_VERSION)
  java:
    runtime: $(DOCKER_NS)/fabric-javaenv:$(ARCH)-$(PROJECT_VERSION)

보시다시피 JAVA 체인 코드 용기는 거울hyperledger/fabric-javaenv에 의존합니다. 이것은 우리가 많은 강좌에서 초기화 네트워크를 구축할 때 여러분들이 먼저 이 거울을 pull하도록 하는 것을 볼 수 있습니다.

제안서를 외우다

좋은 웹페이지 즐겨찾기