//simulate the proposal by calling the chaincode func (e *Endorser) simulateProposal(ctx context.Context, chainID string, txid string, prop *pb.Proposal, cid *pb.ChaincodeID, txsim ledger.TxSimulator) ([]byte, []byte, *pb.ChaincodeEvent, error) { //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(prop) if err != nil { return nil, nil, nil, err } //---1. check ACL if err = e.checkACL(prop); err != nil { return nil, nil, nil, err } //---2. check ESCC and VSCC for the chaincode if err = e.checkEsccAndVscc(prop); err != nil { return nil, nil, nil, err } //---3. execute the proposal and get simulation results var simResult []byte var resp []byte var ccevent *pb.ChaincodeEvent resp, ccevent, err = e.callChaincode(ctx, chainID, txid, prop, cis, cid, txsim) if err != nil { return nil, nil, nil, err } if txsim != nil { if simResult, err = txsim.GetTxSimulationResults(); err != nil { return nil, nil, nil, err } } return resp, simResult, ccevent, nil }
// deploySysCC deploys the given system chaincode on a chain func deploySysCC(chainID string, syscc *SystemChaincode) error { if !syscc.Enabled || !isWhitelisted(syscc) { sysccLogger.Info(fmt.Sprintf("system chaincode (%s,%s) disabled", syscc.Name, syscc.Path)) return nil } if chainID == "" && !syscc.ChainlessCC { return fmt.Errorf("cannot deploy system chaincode %s without chain id", syscc.Name) } else if chainID != "" && syscc.ChainlessCC { return fmt.Errorf("cannot deploy chainless system chaincode %s with chain id %s", syscc.Name, chainID) } var err error ctxt := context.Background() if !syscc.ChainlessCC { lgr := peer.GetLedger(chainID) var txsim ledger.TxSimulator if txsim, err = lgr.NewTxSimulator(); err != nil { return err } ctxt = context.WithValue(ctxt, TXSimulatorKey, txsim) defer txsim.Done() } chaincodeID := &pb.ChaincodeID{Path: syscc.Path, Name: syscc.Name} spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_Type(pb.ChaincodeSpec_Type_value["GOLANG"]), ChaincodeID: chaincodeID, CtorMsg: &pb.ChaincodeInput{Args: syscc.InitArgs}} // First build and get the deployment spec chaincodeDeploymentSpec, err := buildSysCC(ctxt, spec) if err != nil { sysccLogger.Error(fmt.Sprintf("Error deploying chaincode spec: %v\n\n error: %s", spec, err)) return err } txid := util.GenerateUUID() cccid := NewCCContext(chainID, chaincodeDeploymentSpec.ChaincodeSpec.ChaincodeID.Name, "", txid, true, nil) _, _, err = Execute(ctxt, cccid, chaincodeDeploymentSpec) sysccLogger.Infof("system chaincode %s/%s(%s) deployed", syscc.Name, chainID, syscc.Path) return err }
func isTxValidForVscc(payload *common.Payload, envBytes []byte) error { // TODO: Extract the VSCC/policy from LCCC as soon as this is ready vscc := "vscc" chainName := payload.Header.ChainHeader.ChainID if chainName == "" { err := fmt.Errorf("transaction header does not contain an chain ID") logger.Errorf("%s", err) return err } txid := "N/A" // FIXME: is that appropriate? // build arguments for VSCC invocation // args[0] - function name (not used now) // args[1] - serialized Envelope args := [][]byte{[]byte(""), envBytes} // create VSCC invocation proposal vsccCis := &pb.ChaincodeInvocationSpec{ChaincodeSpec: &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG, ChaincodeID: &pb.ChaincodeID{Name: vscc}, CtorMsg: &pb.ChaincodeInput{Args: args}}} prop, err := putils.CreateProposalFromCIS(txid, chainName, vsccCis, []byte("")) if err != nil { logger.Errorf("Cannot create a proposal to invoke VSCC, err %s\n", err) return err } // get context for the chaincode execution var txsim ledger.TxSimulator lgr := peer.GetLedger(chainName) txsim, err = lgr.NewTxSimulator() if err != nil { logger.Errorf("Cannot obtain tx simulator, err %s\n", err) return err } defer txsim.Done() ctxt := context.WithValue(context.Background(), chaincode.TXSimulatorKey, txsim) cccid := chaincode.NewCCContext(chainName, vscc, "", txid, true, prop) // invoke VSCC _, _, err = chaincode.ExecuteChaincode(ctxt, cccid, args) if err != nil { logger.Errorf("VSCC check failed for transaction, error %s", err) return err } return nil }
// TransferMarble simulates transfer transaction func (marbleApp *MarbleApp) TransferMarble(args []string) (*common.Envelope, error) { // 0 1 // "name", "bob" if len(args) < 2 { return nil, errors.New("Incorrect number of arguments. Expecting 2") } marbleName := args[0] marbleNewOwner := args[1] logger.Debugf("===COUCHDB=== Entering ----------TransferMarble----------") var txSimulator ledger.TxSimulator var err error if txSimulator, err = marbleApp.ledger.NewTxSimulator(); err != nil { return nil, err } defer txSimulator.Done() marbleBytes, err := txSimulator.GetState(marbleApp.name, marbleName) logger.Debugf("===COUCHDB=== marbleBytes is: %v", marbleBytes) if marbleBytes != nil { jsonString := string(marbleBytes[:]) logger.Debugf("===COUCHDB=== TransferMarble() Retrieved jsonString: \n %s", jsonString) } theMarble := Marble{} json.Unmarshal(marbleBytes, &theMarble) //Unmarshal JSON bytes into a Marble struct logger.Debugf("===COUCHDB=== theMarble after unmarshal: %v", theMarble) logger.Debugf("===COUCHDB=== Setting the owner to: %s", marbleNewOwner) theMarble.User = marbleNewOwner //change the user theMarble.Txid = "tx000000000000002" // COUCHDB hardcode a txid for now for demo purpose updatedMarbleBytes, _ := json.Marshal(theMarble) if updatedMarbleBytes != nil { updatedJsonString := string(updatedMarbleBytes[:]) logger.Debugf("===COUCHDB=== updatedJsonString:\n %s", updatedJsonString) } err = txSimulator.SetState(marbleApp.name, marbleName, updatedMarbleBytes) if err != nil { return nil, err } var txSimulationResults []byte if txSimulationResults, err = txSimulator.GetTxSimulationResults(); err != nil { return nil, err } logger.Debugf("===COUCHDB=== TransferMarble() simulation done, packaging into a transaction...") tx := constructTransaction(txSimulationResults) return tx, nil }
// CreateMarble simulates init transaction func (marbleApp *MarbleApp) CreateMarble(args []string) (*common.Envelope, error) { // 0 1 2 3 // "asdf", "blue", "35", "bob" logger.Debugf("===COUCHDB=== Entering ----------CreateMarble()----------") marbleName := args[0] marbleJsonBytes, err := init_marble(args) if err != nil { return nil, err } var txSimulator ledger.TxSimulator if txSimulator, err = marbleApp.ledger.NewTxSimulator(); err != nil { return nil, err } defer txSimulator.Done() txSimulator.SetState(marbleApp.name, marbleName, marbleJsonBytes) var txSimulationResults []byte if txSimulationResults, err = txSimulator.GetTxSimulationResults(); err != nil { return nil, err } logger.Debugf("===COUCHDB=== CreateMarble() simulation done, packaging into a transaction...") tx := constructTransaction(txSimulationResults) logger.Debugf("===COUCHDB=== Exiting CreateMarble()") return tx, nil }
func endTxSimulation(chainID string, txsim ledger.TxSimulator, payload []byte, commit bool, prop *pb.Proposal) error { txsim.Done() if lgr := peer.GetLedger(chainID); lgr != nil { if commit { var txSimulationResults []byte var err error //get simulation results if txSimulationResults, err = txsim.GetTxSimulationResults(); err != nil { return err } // assemble a (signed) proposal response message resp, err := putils.CreateProposalResponse(prop.Header, prop.Payload, txSimulationResults, nil, nil, signer) if err != nil { return err } // get the envelope env, err := putils.CreateSignedTx(prop, signer, resp) if err != nil { return err } envBytes, err := putils.GetBytesEnvelope(env) if err != nil { return err } //create the block with 1 transaction block := common.NewBlock(1, []byte{}) block.Data.Data = [][]byte{envBytes} //commit the block if err := lgr.Commit(block); err != nil { return err } } } return nil }
// Init simulates init transaction func (app *App) Init(initialBalances map[string]int) (*common.Envelope, error) { var txSimulator ledger.TxSimulator var err error if txSimulator, err = app.ledger.NewTxSimulator(); err != nil { return nil, err } defer txSimulator.Done() for accountID, bal := range initialBalances { txSimulator.SetState(app.name, accountID, toBytes(bal)) } var txSimulationResults []byte if txSimulationResults, err = txSimulator.GetTxSimulationResults(); err != nil { return nil, err } tx := constructTransaction(txSimulationResults) return tx, nil }
// ProcessProposal process the Proposal func (e *Endorser) ProcessProposal(ctx context.Context, signedProp *pb.SignedProposal) (*pb.ProposalResponse, error) { // at first, we check whether the message is valid prop, _, hdrExt, err := peer.ValidateProposalMessage(signedProp) if err != nil { return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err } hdr, err := putils.GetHeader(prop.Header) if err != nil { return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err } chainID := hdr.ChainHeader.ChainID //chainless MSPs have "" chain name ischainless := chaincode.IsChainlessSysCC(hdrExt.ChaincodeID.Name) //chainID should be empty for chainless SysCC (such as CSCC for Join proposal) and for //nothing else if chainID == "" && !ischainless { err = fmt.Errorf("chainID not provided for chaincode %s", hdrExt.ChaincodeID.Name) return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err } else if chainID != "" && ischainless { err = fmt.Errorf("chainID %s provided for a chainless syscc", hdrExt.ChaincodeID.Name) return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err } //TODO check for uniqueness of prop.TxID with ledger txid := hdr.ChainHeader.TxID if txid == "" { err = fmt.Errorf("Invalid txID") return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err } // obtaining once the tx simulator for this proposal. This will be nil // for chainless proposals var txsim ledger.TxSimulator if chainID != "" { if txsim, err = e.getTxSimulator(chainID); err != nil { return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err } defer txsim.Done() } //this could be a request to a chainless SysCC // TODO: if the proposal has an extension, it will be of type ChaincodeAction; // if it's present it means that no simulation is to be performed because // we're trying to emulate a submitting peer. On the other hand, we need // to validate the supplied action before endorsing it //1 -- simulate //TODO what do we do with response ? We need it for Invoke responses for sure //Which field in PayloadResponse will carry return value ? result, simulationResult, ccevent, err := e.simulateProposal(ctx, chainID, txid, prop, hdrExt.ChaincodeID, txsim) if err != nil { return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err } //2 -- endorse and get a marshalled ProposalResponse message var pResp *pb.ProposalResponse //TODO till we implement global ESCC, CSCC for system chaincodes //chainless proposals (such as CSCC) don't have to be endorsed if ischainless { pResp = &pb.ProposalResponse{Response: &pb.Response{}} } else { pResp, err = e.endorseProposal(ctx, chainID, txid, prop, simulationResult, ccevent, hdrExt.PayloadVisibility, hdrExt.ChaincodeID, txsim) if err != nil { return &pb.ProposalResponse{Response: &pb.Response{Status: 500, Message: err.Error()}}, err } } //TODO what do we do with response ? We need it for Invoke responses for sure // Set the proposal response payload - it // contains the "return value" from the // chaincode invocation pResp.Response.Payload = result return pResp, nil }
// TransferFunds simulates a transaction for transferring fund from fromAccount to toAccount func (app *App) TransferFunds(fromAccount string, toAccount string, transferAmt int) (*common.Envelope, error) { // act as endorsing peer shim code to simulate a transaction on behalf of chaincode var txSimulator ledger.TxSimulator var err error if txSimulator, err = app.ledger.NewTxSimulator(); err != nil { return nil, err } defer txSimulator.Done() var balFromBytes []byte if balFromBytes, err = txSimulator.GetState(app.name, fromAccount); err != nil { return nil, err } balFrom := toInt(balFromBytes) if balFrom-transferAmt < 0 { return nil, fmt.Errorf("Not enough balance in account [%s]. Balance = [%d], transfer request = [%d]", fromAccount, balFrom, transferAmt) } var balToBytes []byte if balToBytes, err = txSimulator.GetState(app.name, toAccount); err != nil { return nil, err } balTo := toInt(balToBytes) txSimulator.SetState(app.name, fromAccount, toBytes(balFrom-transferAmt)) txSimulator.SetState(app.name, toAccount, toBytes(balTo+transferAmt)) var txSimulationResults []byte if txSimulationResults, err = txSimulator.GetTxSimulationResults(); err != nil { return nil, err } // act as endorsing peer to create an Action with the SimulationResults // then act as SDK to create a Transaction with the EndorsedAction tx := constructTransaction(txSimulationResults) return tx, nil }