github.com/muhammedhassanm/blockchain@v0.0.0-20200120143007-697261defd4d/blockapps-ba-master/server/lib/project/projectManager.js (about)

     1  const ba = require('blockapps-rest');
     2  const rest = ba.rest;
     3  const util = ba.common.util;
     4  const config = ba.common.config;
     5  const Promise = ba.common.Promise;
     6  const BigNumber = ba.common.BigNumber;
     7  const constants = ba.common.constants;
     8  
     9  const contractName = 'ProjectManager';
    10  const contractFilename = `${config.libPath}/project/contracts/ProjectManager.sol`;
    11  
    12  const ErrorCodes = rest.getEnums(`${config.libPath}/common/ErrorCodes.sol`).ErrorCodes;
    13  const ProjectState = ba.rest.getEnums(`${config.libPath}/project/contracts/ProjectState.sol`).ProjectState;
    14  const ProjectEvent = ba.rest.getEnums(`${config.libPath}/project/contracts/ProjectEvent.sol`).ProjectEvent;
    15  const BidState = ba.rest.getEnums(`${config.libPath}/bid/contracts/BidState.sol`).BidState;
    16  const projectContractName = require('./project').contractName;
    17  
    18  function* uploadContract(admin, args) {
    19    const contract = yield rest.uploadContract(admin, contractName, contractFilename, args);
    20    yield compileSearch(contract);
    21    contract.src = 'removed';
    22    return setContract(admin, contract);
    23  }
    24  
    25  function setContract(admin, contract) {
    26    contract.getState = function* () {
    27      return yield rest.getState(contract);
    28    }
    29    contract.createProject = function* (args) {
    30      return yield createProject(admin, contract, args);
    31    }
    32    contract.getProject = function* (name) {
    33      return yield getProject(admin, contract, name);
    34    }
    35    contract.getProjects = function* (name) {
    36      return yield getProjects(contract);
    37    }
    38    contract.exists = function* (name) {
    39      return yield exists(admin, contract, name);
    40    }
    41    contract.getProjectsByBuyer = function* (buyer) {
    42      return yield getProjectsByBuyer(contract, buyer);
    43    }
    44    contract.getProjectsBySupplier = function* (supplier) {
    45      return yield getProjectsBySupplier(contract, supplier);
    46    }
    47    contract.getProjectsByName = function* (name) {
    48      return yield getProjectsByName(contract, name);
    49    }
    50    contract.getProjectsByState = function* (state) {
    51      return yield getProjectsByState(contract, state);
    52    }
    53    contract.handleEvent = function* (name, projectEvent) {
    54      return yield handleEvent(admin, contract, name, projectEvent);
    55    }
    56    contract.createBid = function* (name, supplier, amount) {
    57      return yield createBid(admin, contract, name, supplier, amount);
    58    }
    59    contract.acceptBid = function* (buyer, bidId, name) {
    60      return yield acceptBid(admin, contract, buyer, bidId, name);
    61    }
    62    contract.settleProject = function* (projectName, supplierAddress, bidAddress) {
    63      return yield settleProject(admin, contract, projectName, supplierAddress, bidAddress);
    64    }
    65    contract.getAcceptedBid = getAcceptedBid;
    66  
    67    return contract;
    68  }
    69  
    70  function* compileSearch(contract) {
    71    rest.verbose('compileSearch', contract.codeHash);
    72    if (yield rest.isSearchable(contract.codeHash)) {
    73      return;
    74    }
    75    // compile dependencies
    76    const bidJs = require('../bid/bid');
    77    const userJs = require('../user/user');
    78    const projectJs = require('./project');
    79    // compile
    80    const searchable = [bidJs.contractName, projectJs.contractName, contractName];
    81    return yield rest.compileSearch(searchable, contractName, contractFilename);
    82  }
    83  
    84  // throws: ErrorCodes
    85  // returns: record from search
    86  function* createProject(admin, contract, args) {
    87    rest.verbose('createProject', {admin, args});
    88    // function createProject(
    89    //   string name,
    90    //   string buyer,
    91    //   string description,
    92    //   string spec,
    93    //   uint price,
    94    //   uint created,
    95    //   uint targetDelivery
    96    // ) returns (ErrorCodes) {
    97    const method = 'createProject';
    98  
    99    const result = yield rest.callMethod(admin, contract, method, args);
   100    const errorCode = parseInt(result[0]);
   101    if (errorCode != ErrorCodes.SUCCESS) {
   102      throw new Error(errorCode);
   103    }
   104    // get the contract data from search
   105    const project = yield getProject(admin, contract, args.name);
   106    return project;
   107  }
   108  
   109  // throws: ErrorCodes
   110  // returns: record from search
   111  function* createBid(admin, contract, name, supplier, amount) {
   112    rest.verbose('createBid', {name, supplier, amount});
   113    // function createBid(string name, string supplier, uint amount) returns (ErrorCodes, uint) {
   114    const method = 'createBid';
   115    const args = {
   116      name: name,
   117      supplier: supplier,
   118      amount: amount,
   119    };
   120  
   121    const result = yield rest.callMethod(admin, contract, method, args);
   122    const errorCode = parseInt(result[0]);
   123    if (errorCode != ErrorCodes.SUCCESS) {
   124      throw new Error(errorCode);
   125    }
   126    const bidId = result[1];
   127    // block until the contract shows up in search
   128    const bid = (yield rest.waitQuery(`Bid?id=eq.${bidId}`, 1))[0];
   129    return bid;
   130  }
   131  
   132  // throws: ErrorCodes
   133  function* acceptBid(admin, contract, buyer, bidId, name) {   // FIXME should go into the contract
   134    rest.verbose('acceptBid', {admin, buyer, bidId, name});
   135    const bids = yield getBidsByName(name);
   136    if (bids.length < 1) {
   137      throw new Error(ErrorCodes.NOT_FOUND);
   138    }
   139    // find the winning bid
   140    const winningBid = bids.filter(bid => {
   141      return bid.id == bidId;
   142    })[0];
   143    // accept the bid (will transfer funds from buyer to bid contract)
   144    try {
   145      yield setBidState(buyer, winningBid.address, BidState.ACCEPTED, winningBid.amount);
   146    } catch(error) {
   147      // check insufficient balance
   148      console.log(error.status);
   149      if (error.status == 400) {
   150        throw new Error(ErrorCodes.INSUFFICIENT_BALANCE);
   151      }
   152      throw error;
   153    }
   154    // reject all other bids
   155    for (let bid of bids) {
   156      if (bid.id != bidId) {
   157        yield setBidState(buyer, bid.address, BidState.REJECTED, 0); // REJECT
   158      }
   159    }
   160    const result = yield handleEvent(admin, contract, name, ProjectEvent.ACCEPT);
   161    return result;
   162  }
   163  
   164  function* setBidState(buyer, bidAddress, state, valueEther) {
   165    rest.verbose('setBidState', {buyer, bidAddress, state, valueEther});
   166    const contract = {
   167      name: 'Bid',
   168      address: bidAddress,
   169    }
   170  
   171    // function setBidState(address bidAddress, BidState state) returns (ErrorCodes) {
   172    const method = 'setBidState';
   173    const args = {
   174      newState: state,
   175    };
   176    // the api is expecting the buyers bloc-account address (not the app-user address)
   177    const buyerAccount = {
   178      name: buyer.username,
   179      password: buyer.password,
   180      address: buyer.account,
   181    };
   182  
   183    const valueWei = new BigNumber(valueEther).mul(constants.ETHER);
   184    const result = yield rest.callMethod(buyerAccount, contract, method, args, valueWei);
   185    const errorCode = parseInt(result[0]);
   186    if (errorCode != ErrorCodes.SUCCESS) {
   187      throw new Error(errorCode);
   188    }
   189  }
   190  
   191  function* settleProject(admin, contract, projectName, supplierAddress, bidAddress) {
   192    rest.verbose('settleProject', {projectName, supplierAddress, bidAddress});
   193    // function settleProject(string name, address supplierAddress, address bidAddress) returns (ErrorCodes) {
   194    const method = 'settleProject';
   195    const args = {
   196      name: projectName,
   197      supplierAddress: supplierAddress,
   198      bidAddress: bidAddress,
   199    };
   200  
   201    const result = yield rest.callMethod(admin, contract, method, args);
   202    const errorCode = parseInt(result[0]);
   203    if (errorCode != ErrorCodes.SUCCESS) {
   204      throw new Error(errorCode);
   205    }
   206  }
   207  
   208  function* getBid(bidId) {
   209    rest.verbose('getBid', bidId);
   210    return (yield rest.waitQuery(`Bid?id=eq.${bidId}`,1))[0];
   211  }
   212  
   213  function* getBidsByName(name) {
   214    rest.verbose('getBidsByName', name);
   215    return yield rest.query(`Bid?name=eq.${encodeURIComponent(name)}`);
   216  }
   217  
   218  function* getBidsBySupplier(supplier) {
   219    rest.verbose('getBidsBySupplier', supplier);
   220    return yield rest.query(`Bid?supplier=eq.${supplier}`);
   221  }
   222  
   223  // throws: ErrorCodes
   224  function* getAcceptedBid(projectName) {
   225    rest.verbose('getAcceptedBid', projectName);
   226    // get project bids
   227    const bids = yield getBidsByName(projectName);
   228    // extract the supplier out of the accepted bid
   229    const accepted = bids.filter(bid => {
   230      return parseInt(bid.state) === BidState.ACCEPTED;
   231    });
   232    // not found
   233    if (accepted.length == 0) {
   234      throw new Error(ErrorCodes.NOT_FOUND);
   235    }
   236    // more then one
   237    if (accepted.length > 1) {
   238      throw new Error(ErrorCodes.ERROR);
   239    }
   240    return accepted[0];
   241  }
   242  
   243  function* exists(admin, contract, name) {
   244    rest.verbose('exists', name);
   245    // function exists(string name) returns (bool) {
   246    const method = 'exists';
   247    const args = {
   248      name: name,
   249    };
   250    const result = yield rest.callMethod(admin, contract, method, args);
   251    const exists = (result[0] === true);
   252    return exists;
   253  }
   254  
   255  function* getProject(admin, contract, name) {
   256    rest.verbose('getProject', name);
   257    // function getProject(string name) returns (address) {
   258    const method = 'getProject';
   259    const args = {
   260      name: name,
   261    };
   262  
   263    // returns address
   264    const address = (yield rest.callMethod(admin, contract, method, args))[0];
   265    // if not found - throw
   266    if (address == 0) {
   267      throw new Error(ErrorCodes.NOT_FOUND);
   268    }
   269    // found - query for the full record
   270    const project = (yield rest.waitQuery(`${projectContractName}?address=eq.${address}`, 1))[0];
   271    return project;
   272  }
   273  
   274  function* getProjects(contract) {
   275    rest.verbose('getProjects');
   276    const state = yield rest.getState(contract);
   277    const projects = state.projects.slice(1); // remove the first '0000' project
   278    const csv = util.toCsv(projects); // generate csv string
   279    const results = yield rest.query(`${projectContractName}?address=in.${csv}`);
   280    return results;
   281  }
   282  
   283  function* getProjectsByBuyer(contract, buyer) {
   284    rest.verbose('getProjectsByBuyer', buyer);
   285    const projects = yield getProjects(contract);
   286    const filtered = projects.filter(function(project) {
   287      return project.buyer === buyer;
   288    });
   289    return filtered;
   290  }
   291  
   292  function* getProjectsByState(contract, state) {
   293    rest.verbose('getProjectsByState', state);
   294    const projects = yield getProjects(contract);
   295    const filtered = projects.filter(function(project) {
   296      return parseInt(project.state) == state;
   297    });
   298    return filtered;
   299  }
   300  
   301  function* getProjectsBySupplier(contract, supplier) {
   302    rest.verbose('getProjectsBySupplier', supplier);
   303    const bids = yield getBidsBySupplier(supplier);
   304    const names = bids.map(function(bid) {
   305      return bid.name;
   306    });
   307    const projects = yield getProjectsByName(contract, names);
   308    return projects;
   309  }
   310  
   311  function* getProjectsByName(contract, names) {
   312    rest.verbose('getProjectsByName', names);
   313    if (names.length == 0) {
   314      return [];
   315    }
   316    // the url might get too long, so the query is broken to multipart
   317    const MAX = 50; // max names to list in one REST call
   318    const parts = Math.ceil(names.length/MAX);
   319    let results = [];
   320    for (let i = 0; i < parts; i++) {
   321      const start = i*MAX;
   322      const end = (i<parts-1) ? (i+1)*MAX : names.length;
   323      const csv = util.toCsv(names.slice(start, end)); // generate csv string
   324      const partialResults = yield rest.query(`${projectContractName}?name=in.${encodeURIComponent(csv)}`); // get a part
   325      results = results.concat(partialResults); // add to the results
   326    }
   327    return results;
   328  }
   329  
   330  function* handleEvent(admin, contract, name, projectEvent) {
   331    rest.verbose('handleEvent', {admin, name, projectEvent});
   332  
   333    const project = yield getProject(admin, contract, name);
   334    // function handleEvent(address projectAddress, ProjectEvent projectEvent) returns (ErrorCodes, ProjectState) {
   335    const method = 'handleEvent';
   336    const args = {
   337      projectAddress: project.address,
   338      projectEvent: projectEvent,
   339    };
   340    const result = yield rest.callMethod(admin, contract, method, args);
   341    const errorCode = parseInt(result[0]);
   342    if (errorCode != ErrorCodes.SUCCESS) {
   343      throw new Error(errorCode);
   344    }
   345    const newState = parseInt(result[1]);
   346    return newState;
   347  }
   348  
   349  module.exports = {
   350    contractName:contractName,
   351    compileSearch: compileSearch,
   352    uploadContract: uploadContract,
   353    setContract: setContract,
   354  
   355    getAcceptedBid: getAcceptedBid,
   356    getBid: getBid,
   357    getBidsByName: getBidsByName,
   358    getBidsBySupplier: getBidsBySupplier,
   359    getProjectsByState: getProjectsByState,
   360  };