├── .gitattributes ├── .gitignore ├── .travis.yml ├── Dockerfile ├── LICENSE ├── README.md ├── app.json ├── bin ├── methods.js └── run.js ├── client ├── colu.client.js └── colu.client.min.js ├── gulpfile.js ├── package.json ├── src └── colu.js ├── test ├── test-rpc.js ├── test-utils.js └── test.js └── tsd.json /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | 29 | test.html 30 | properties.conf 31 | bin/playground.js 32 | test/settings.js 33 | test/settings.json 34 | typings/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - stable 4 | - '4.6.1' 5 | os: 6 | - linux 7 | - osx 8 | before_install: 9 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then git clone https://github.com/creationix/nvm.git /tmp/.nvm; fi 10 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then source /tmp/.nvm/nvm.sh; fi 11 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then nvm install $TRAVIS_NODE_VERSION; fi 12 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then nvm use --delete-prefix $TRAVIS_NODE_VERSION; fi 13 | install: 14 | - npm install 15 | before_script: 16 | - npm install -g istanbul 17 | - npm install -g mocha 18 | - npm install -g coveralls 19 | script: 20 | - npm test 21 | after_script: 22 | - npm run coveralls 23 | services: 24 | - redis-server -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:latest 2 | 3 | # update Linux OS 4 | RUN apt-get -y update 5 | 6 | # install redis 7 | RUN wget http://download.redis.io/redis-stable.tar.gz 8 | RUN tar xvzf redis-stable.tar.gz 9 | RUN cd redis-stable && make 10 | RUN cp redis-stable/src/redis-server /usr/local/bin/ 11 | RUN cp redis-stable/src/redis-cli /usr/local/bin/ 12 | RUN mkdir -p /etc/redis 13 | RUN mkdir -p /var/redis 14 | RUN mkdir -p /var/redis/6379 15 | RUN cp redis-stable/utils/redis_init_script /etc/init.d/redis_6379 16 | RUN update-rc.d redis_6379 defaults 17 | RUN chmod -R a+w /var/redis/ 18 | RUN chmod -R a+w /var/run/ 19 | RUN chmod -R a+w /var/log/ 20 | 21 | # install colu 22 | RUN npm i -g colu 23 | 24 | # set colu env vars. in mainnet, change the network (testnet -> mainnet) and add your API key (https://www.colu.co/getapikey) 25 | ENV COLU_SDK_NETWORK testnet 26 | ENV COLU_SDK_API_KEY your_api_key_here 27 | ENV COLU_SDK_RPC_SERVER_HTTP_PORT 80 28 | ENV COLU_SDK_RPC_SERVER_HOST 0.0.0.0 29 | ENV COLU_SDK_REDIS_PORT 6379 30 | 31 | # start redis and colu servers 32 | CMD redis-server & node $(which colu) -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | These Terms and Conditions stipulated herein are a binding agreement between Colu Technologies Ltd. (“Colu”), a duly incorporated Israeli corporation, and yourself, either a natural person or a designated representative acting on behalf of a corporation. 2 | 3 | The “Colu Platform”, which includes the Colu API, Colu SDK and Colu engine allows you to issue and manage Assets (as defined later in this document) based on the core blockchain technology, which is not managed or owned by Colu. Using the Colu Platform you may issue Assets, manage them, revoke them or otherwise modify their properties. 4 | 5 | However, as they are based entirely on the blockchain, which is not managed by Colu, these Assets may be modified and managed also by the use of the bitcoin blockchain, by yourself. 6 | 7 | Colu encourages an open platform, and the use of its API in order to provide the services, but does not allow illegal or harmful use. Therefore, you are prohibited from using the Colu Platform in a manner which violates the law, or in a manner which might harm Colu and its property. 8 | 9 | The Colu Platform comes with absolutely no warranty, and is meant to be used AS-IS. 10 | 11 | Please read the full Terms and Conditions, as they are the sole legal document, and this preamble shall not be used for interpretation. 12 | 13 | Registration, Eligibility and Application Terms: The following article sets forth Colu’s eligibility and registration terms. If you are not eligible to register, please do not perform the activation of your account. Should you be found later to be non-eligible, then Colu may terminate your account without notice. 14 | General Information Required To Register: The following information is a prerequisite to enter into these Terms and Conditions: your full name, the Company which you represent (if applicable), and your email. Colu may later request additional information, in order to verify your identity and/or in order to provide additional services. 15 | One Account Per One Entity: Colu’s current policy is that each legal entity shall have only one account with one login. This means that if you are acting on behalf of a corporation, then you may share this account between the corporation’s staff. However, it is your sole responsibility to keep this account secure. 16 | If acting on behalf of a corporation: If you are acting on behalf of a corporation, then you are the authorized entity to enter into these terms and are acting on behalf of such corporation. 17 | Solvency: You are solvent and have not filed for bankruptcy, liquidation, debt arrangement or otherwise lost your ability into entering into agreements such as these terms. 18 | Not Representing An Enemy State: You do not reside in North Korea, Libya, Syria, Iran, or any other jurisdiction where Colu cannot enforce its terms. 19 | Adult: If you are a natural person, you are either over 18, or you are over 13 years of age and have obtained your parental consent. 20 | Password: When using the Colu Platform, you may be required to select a password. The password shall be hashed on Colu’s servers and may be used to encrypt your private key for issuing the Assets. Please take into consideration that if you lose both your password and backup files, then you may not be able to recover access to your Assets, as they are controlled by this private key. 21 | Notification: You are required to inform Colu, immediately, upon any case where you believe that your account was breached and in any case you are in knowledge of any unauthorized use of any Colu account. 22 | Colu’s Liability: Colu shall not be liable in any case where you did not adhere to these security guidelines and shall not be able to assist you in any manner should you refrain from using Colu’s best practices, as published from time to time. 23 | Backup: Colu may allow you to backup your private key to your computer or any other third party service. It shall be your sole responsibility to keep such backup secure, as it allows access to your Assets. 24 | API Key: Colu may provide you with an API (Application Programming Interface) key. Such API key may be used to issue and manage your Assets. It shall be your sole responsibility to keep the API key secure, as it allows access to your Assets. 25 | Activation Link: Following your initial registration, you may be required to activate your account and validate your email by accessing an activation link sent to you. 26 | Services: The following services are a part of the Colu Platform and fully owned by Colu, or duly licensed to it; where Colu may add or deprecate services from time to time, and may withhold a portion of the services. Each of the services may be referred to as a part of the Colu Platform, or as a “Service”. 27 | Issue Assets: Colu may allow you to issue Assets. An “Asset” is a virtual token based on the blockchain technology, which may represent either a token granting access to a specific venue and time (such as a movie ticket), a control of a physical Asset (such as a “certificate of authenticity” for a rare baseball card), or a membership card (such as a gym membership). 28 | Issuance of Assets: Colu may allow you to issue Assets, either using your own set of rules and metadata stored on the Assets (such as the seat of a movie ticket, or dates where the gym membership is applicable) and to control the transferability of the Asset, its divisibility (such as half of a movie ticket versus half ownership of a baseball card), it’s original issuer (yourself), and other options. 29 | Access Assets: Colu may also allow you to issue access Assets. An access Asset is used for online and offline authentication and identity management (such as a gym membership or accessing a website through two-factor authentication). 30 | Use Of Templates: Colu may allow you to use templates generated either by Colu or its users for issuing your Assets. These templates may be provided for free or against a fee, and may or may not be available for modification. 31 | Migration: If you previously issued Assets on different protocols, then Colu may allow you to migrate your Assets to the Colu Platform. 32 | Send Asset: The Colu Platform may allow you to send Assets to users who installed the Colu application or to addresses on the blockchain. Please note that while it is possible to send Assets on top of the blockchain to non-Colu users, it may cause the Asset to be unusable or lost forever. 33 | Manage Asset: Colu may allow you to manage your Assets, and add or remove information from them, as well as adding a data layer on top of such Assets, where you can add additional data to your Asset which is not stored on the blockchain. 34 | Colu Code: Colu may provide you with a specific software code, which includes Colu's proprietary source code, for either the Colu SDK or the Colu Application ("Colu Software"), to be used to integrate the Colu Platform and/or access the Colu API with your products. When doing so, you shall be licensed a limited, non-exclusive, non-assignable, non-transferrable, revocable, temporary, personal license to distribute the Colu Software solely as a part of your own application, provided that your end-users shall accept the Colu application terms of service and shall hold Colu as harmless and exempt from any warranty; moreover, Colu should be credited in all distribution of your application. You may publish small portions of the Colu source code as examples, but you may not distribute the Colu Software source code to any third party, nor may you allow others to create software using the Colu Software for redistribution. You may amend the Colu Software source code as a part of your application, and distribute it to your end-users as a part of a larger work. However, apart where specifically licensed, you are prohibited from using, distributing, publishing, making derivative works, changing, reverse engineering, decompiling or otherwise manipulating the Colu Software. The Colu Software may be offered to you free of charge or against payment, where this License may be revoked at any time by Colu. 35 | Revocation: Colu may allow you to revoke specific Assets (such as lost movie tickets), and to either replace or reissue Assets against revoked Assets. 36 | Analytics: Colu may allow you to review statistical information about the use of your issued Assets. This may include the use of the Assets by other users, the transaction information on the blockchain and your internal use of the Assets. 37 | API: Colu may avail you to API access to the Colu Platform, where you may be able to perform some of the services provided by Colu. In such case, Colu may also monitor and/or review your use of the API as a part of your use of the services. 38 | Generate Templates: Colu may allow you to generate templates for managing Assets, where such template may either be used internally by yourself (such as a theater generating a movie ticket template) or shared with others through the Colu Platform (such as a high school issuing prom tickets and allowing nearby schools to copy it). 39 | Send Messages: Colu may allow you to send messages to Asset holders over the blockchain. 40 | Precaution: Please note that as the actions performed on the Colu Platform are based on the blockchain technology, anyone who holds a copy of your public key may view your transactions. 41 | Fees: In consideration of its services, Colu may charge you fees as set forth in Colu’s fee schedule, which may be amended from time to time. Such fees may include set-up fees, monthly subscriptions, usage fees or overage fees. Colu may determine the means which the fees shall be paid by, such as credit card, PayPal or bitcoins. 42 | Refunds: Colu’s services are provided to you where your purchase of Colu’s services is a payment for services, intangible goods, tailor made goods, or virtual property. Therefore, Colu is not obligated to refund or reimburse you in any way. Colu may personally consider each refund request and may refund according to its sole discretion. 43 | Support: Colu currently provides limited support, where such support is provided by email, during regular business hours. Colu may provide extended support services, either for free or for additional charges, and may provide professional services as part of such support (such as assisting in developing Colu Platform applications). 44 | User Generated Content: In some cases, Colu may allow you to share content you created with others. For the purpose of these Terms and Conditions, the term “Content” shall refer to: templates you created through the Colu Platform, your Asset’s logo and description, images you publish through the Platform, messages you transmit over the Colu Platform and metadata you store through the Colu Platform. 45 | License To Use: You hereby license Colu an irrevocable, permanent, unlimited, world-wide, royalty-free, sublicensable, non-exclusive license to use your Content and to allow users to use your Content according to the functionality of the Colu Platform, all as required by the functionality of the Colu Platform. 46 | Warranty: You hereby warrant that you are either the sole proprietor or a designated licensee of any Content you submit through Colu Platform and that no other party’s rights are infringed or violated by using the Content through the Colu Platform. You moreover warrant that no legal claim, dispute or lawsuit was filed or threatened against you or for using the Content. 47 | Trademarks: If your Content contains a trademark, then you hereby license Colu an irrevocable, permanent, unlimited, world-wide, royalty-free, sublicensable, non-exclusive license to use and display any trademarks associated with your Content according to the functionality of the Colu Platform. 48 | Prescreen: You acknowledge that Colu cannot pre-screen all Content, but that it shall have the right (but not the obligation) in their sole discretion to refuse or remove any Content that is available via the Colu Platform. 49 | Disclosure of Content: You acknowledge and agree that Colu may preserve Content and may also disclose Content if required to do so by law or in the good faith belief that such preservation or disclosure is reasonably necessary to: (a) comply with legal process; (b) enforce these terms; (c) respond to claims that any Content violates the rights of third parties; or (d) protect the rights, property, or personal safety of Colu, its users, and the public. You understand that the technical processing and transmission of the Content through the Colu Platform, may involve: (i) transmissions over various networks; and (ii) changes to conform and adapt to technical requirements of connecting networks or devices. 50 | Harmless: You agree and warrant to hold Colu and its users harmless and to immediately indemnify Colu or its users for any claim of copyright infringement, trademark dilution or patent infringement arising from their use of your Content according to these terms. 51 | Notifications: Should you encounter any Content which you believe to be in violation of any of your rights, good name or copyrights, please file an infringement complaint to Colu’s Content Officer at officer@colu.co. Colu’s officer shall examine your complaint and shall forward it to the user who published said Content for his answer. Should your complaint be false, harassing or in order to prevent legal use of service, you shall bear all liability to compensate the user which you reported as infringing and/or Colu. In your complaint you will be required to inform us with: (i) a written statement regarding which Content infringes your rights and proof that you hold those rights; (ii) what is the exact location or identifier of the Content; and (iii) notification that you believe that the use made by the Content is not considered fair-use, criticism, consumer protest or any other protected speech. 52 | Moderation: Pursuant to any notification of infringement, and promptly thereafter, Colu shall moderate the Content and review your request; Colu shall notify the user who posted the Content on your complaint, including your information and request his response. 53 | Removal or Restoration: Should the user who posted the content fail to respond within 96 hours, Colu shall remove the Content; had he responded, Colu shall inspect his response and had there any material questions of fact or law arise, shall forward his personal information directly to you so you could commence litigation over your complaint. 54 | Indemnification: You hereby warrant and agree to hold Colu harmless and to indemnify Colu for any damage, loss, expense, legal expense or cost incurred as a result of: (i) your posting of any Content in direct violation of these Terms and Conditions, including any false representation; and/or (ii) any claim, complaint or notification filed maliciously or negligently against any user where no such claim had any legal standing. 55 | Acceptable Use Policy: Colu provides the Colu Platform for your bona fide use. You may use the Colu Platform for your own personal (or corporate) use. You shall not resell the Colu Platform, allow third parties to access the Colu Platform and/or information obtained through the Colu Platform or make excessive calls and/or uses of the API and/or Colu Platform. Colu may put a cap on the number of server calls, Asset issuances, Asset activities and/or other functions. 56 | No Malicious Activity: Colu places high value on the safe and bona fide use of the Colu Platform. Each of the following malicious activities shall cause immediate termination of your account and withholding of any Assets: 57 | Spam: you may not use the Colu Platform to spam, nor you may use spam to promote and distribute your Assets. For the purpose of this agreement, “spam” shall mean all and any uninvited or unsolicited postings or communications, including repetitive web-posting, splogs, unsolicited emails, short messages or social network messages, sending of invitations through social networks or end-users’ contacts and engaging in any harassing activity. 58 | Viruses and Malware: you may not use the Colu Platform to distribute Viruses and Malware, nor you may use viruses and malware to distribute Assets. For the purpose of these Terms and Conditions, “Viruses and Malware” shall mean any and all software or code which acts without the end-users’ specific consent and/or performs any activity which was not intended to be performed by the end user, including any secret key-loggers, trojans, back-doors, and including any automated installers and updaters. 59 | Adult Content: you may not use the Colu Platform to distribute Adult Content to end-users, nor may you use Adult Content to distribute Assets, where “Adult Content” shall mean any pornography, obscenity, nudity, or other content which may be harmful to minors. 60 | Tricking Children: Children are considered a sensitive group and are not eligible to enter into agreements without their parents’ consent. Therefore, they cannot agree to Colu’s terms and conditions or install Colu’s application. Therefore, you may not use the Colu Platform to distribute Assets to children nor you may target children to download and install the Colu application. 61 | Fraudulent Activity: You may not use the Colu Platform for fraudulent activity nor you may use fraudulent activity to distribute Assets. Fraudulent activity shall mean any activity where (i) Assets are used for illegal issuance of securities, shares, equity, bond, stock or other forms of property rights; (ii) Assets are used during the holding of an illegal gambling or gaming event; (iii) Assets are used to convey property which is prohibited by law; or (iv) Assets are otherwise used for means which are prohibited by law. 62 | Illegal Activity: you may not use the Colu Platform in any illegal activity nor may you use illegal activity to distribute Assets. Illegal activity shall mean any activity which is prohibited in the jurisdiction where the Assets are used in. 63 | Infringing on Third Party Rights: you may not use the Colu Platform in any manner which infringes third party rights, nor may you infringe any third party rights to distribute Assets. Third party rights shall include, but not be limited to, right for privacy, copyright, patents, trademarks, goodwill, good name and other rights. 64 | Invading Personal Information: You may not share any personal information over the Assets, as they may be viewable by the public through the blockchain. 65 | Warranty: Colu does not warrant for Colu Platform’s quality and supplies it on an “as-is” and “as-available” basis. Your use of the Colu Platform is at your own risk and under your liability. Colu makes no warranty that: (i) the Colu Platform will meet your requirements; (ii) the use of the Colu Platform will be uninterrupted, timely, secure, or error-free; (iii) the results that may be obtained from the use of the Colu Platform will be accurate or reliable; (iv) the quality of any products, services, information, or other material purchased or obtained by you through Colu Platform will meet your expectations; or (v) any errors in the Colu Platform will be corrected. Moreover, you hereby acknowledge that the Colu Platform is in early beta stage and that it is currently under development. You acknowledge that the service may be flawed and that some errors may occur. 66 | Liability: For no case and for no reason shall Colu be held liable for any damage, direct or indirect, consequential, exemplary, physical or special, to you, any other user or any third party due to its misperformance of duties herein. Colu provides the services on an AS-IS basis and shall not be held liable, to the extent permitted by law, by any case of misconduct, negligence, gross negligence, malice or any other mean, to any damages or loss of property, including damages to: your credit on the Colu service, Assets, reputation and business reputation, user account information including login information, loss of profit and loss of good name, all resulting from the use or inability to use Colu services. 67 | Indemnification: You hereby warrant and agree to hold Colu harmless and to indemnify Colu for any damage, loss, expense, legal expense or cost incurred as a result of: (i) Your use of the Colu Platform in direct violation of these Terms and Conditions, including any false representation; and/or (ii) any claim, complaint or notification filed maliciously or negligently against any Colu user where no such claim had any legal standing. 68 | Amending these Terms: Colu may amend these Terms and Conditions from time to time, provided that you shall be informed through electronic communication on such amendment and shall be granted the option to terminate your agreements with Colu by providing a 30 days prior written notice. 69 | Terminating User Accounts: Colu shall have the right to terminate your use of the Colu Platform at any time and by providing a 30 day prior notice. Moreover, Colu may terminate your use of the Colu Platform at any time and with no prior written notice in any case where you breached these Terms and Conditions and such breach may cause Colu irreparable harm. You may terminate your account at any time, but please note that in such case, some of the information and Assets you issued may be lost. 70 | Terminating The Platform: Colu may terminate the Colu Platform at any time, by providing a written notice of 30 days prior to such termination. In such case, Colu shall exercise best efforts to allow you to migrate your Assets to a different platform. 71 | Independent Contractors: The parties enter into these terms as independent contractors. Nothing in these terms shall create any employee-employer relationship, partnership, joint venture, equity holdings or any other legal construction. 72 | Severability: If any provision of these Terms and Conditions shall be held unenforceable by any competent legal authority, it shall not limit the other provisions of these terms. 73 | Governing Law, Jurisdiction, No Class Action: These Terms and Conditions shall be solely governed by the laws of the state of Israel and any dispute arising from it shall be solely brought to the competent courts of the Tel-Aviv district. You hereby warrant and undertake not to initiate any class action lawsuit against Colu, any merchant or user of the service, for any cause and to solely seek your own damages. 74 | Assignment: These Terms and Conditions and any rights and licenses granted hereunder, may not be transferred or assigned by you but may be assigned by Colu without restriction or notification. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Colu-Node.Js 2 | [![Build Status](https://travis-ci.org/Colu-platform/colu-nodejs.svg?branch=master)](https://travis-ci.org/Colu-platform/colu-nodejs) [![Coverage Status](https://coveralls.io/repos/Colu-platform/colu-nodejs/badge.svg?branch=master)](https://coveralls.io/r/Colu-platform/colu-nodejs?branch=master) 3 | [![npm version](https://badge.fury.io/js/colu.svg)](http://badge.fury.io/js/colu) 4 | 5 | [![js-standard-style](https://cdn.rawgit.com/feross/standard/master/badge.svg)](https://github.com/feross/standard) 6 | 7 | ## Using 8 | 9 | ### Installation 10 | 11 | ```sh 12 | $ npm i colu 13 | ``` 14 | 15 | ### Documentation 16 | 17 | Full documentation for this Module can be found here: [http://documentation.colu.co/](http://documentation.colu.co/#1.SDK) 18 | 19 | ### Running as a standalone server 20 | 21 | To run as a standalone server you need to install Node.Js and NPM. 22 | Then install the colu module globaly like this: 23 | 24 | ```sh 25 | $ npm i -g colu 26 | ``` 27 | 28 | Then just run it from the terminal like this: 29 | 30 | ```sh 31 | $ colu 32 | ``` 33 | 34 | And now you can use [JSON-RPC 2.0](http://www.jsonrpc.org/specification) requests to use the Colu SDK. 35 | By default it will be hosted at : 127.0.0.1:80 and will be locked to local host only. 36 | 37 | ## Developing 38 | 39 | ### Testing 40 | 41 | ```sh 42 | $ npm test 43 | ``` 44 | 45 | ### Docker 46 | 47 | #### Build image 48 | ``` 49 | docker build -t colunodejs . 50 | ``` 51 | 52 | #### Run image in a container 53 | 54 | ``` 55 | docker run -p 8080:80 -it colunodejs 56 | ``` 57 | You should now be able to make your api calls to port 8080 on your host machine (or select another port, say 1234, by running instead with `docker run -p 1234:80 -it colunodejs`) 58 | 59 | #### View your container running 60 | ```` 61 | docker ps 62 | ```` 63 | 64 | #### Stopping the container 65 | Since we are running in interactive shell mode `-it` you can stop the container by pressing `CTRL+C` -------------------------------------------------------------------------------- /app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "colu", 3 | "description": "Colu SDK for engine and colored-coins api", 4 | "repository": "https://github.com/Colu-platform/colu-nodejs", 5 | "logo": "http://coloredcoins.org/wordpress/wp-content/uploads/2015/07/icon-colu.png", 6 | "keywords": [ 7 | "colu", 8 | "engine", 9 | "colored", 10 | "coins", 11 | "bitcoin" 12 | ] 13 | } -------------------------------------------------------------------------------- /bin/methods.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | 'issueAsset': { 3 | 'params': ['params'], 4 | 'callback': true 5 | }, 6 | 'sendAsset': { 7 | 'params': ['params'], 8 | 'callback': true 9 | }, 10 | 'burnAsset': { 11 | 'params': ['params'], 12 | 'callback': true 13 | }, 14 | 'getAssets': { 15 | 'callback': true 16 | }, 17 | 'getTransactions': { 18 | 'optional': ['addresses'], 19 | 'callback': true 20 | }, 21 | 'getIssuedAssets': { 22 | 'optional': ['transactions'], 23 | 'callback': true 24 | }, 25 | 'getIssuedAssetsFromTransactions': { 26 | 'params': ['addresses', 'transactions'] 27 | }, 28 | 'getAssetMetadata': { 29 | 'params': ['assetId', 'utxo'], 30 | 'optional': ['full'], 31 | 'callback': true 32 | }, 33 | 'buildTransaction': { 34 | 'params': ['type', 'args'], 35 | 'callback': true 36 | }, 37 | 'transmit': { 38 | 'params': ['signedTxHex', 'lastTxid'], 39 | 'callback': true 40 | }, 41 | 42 | /* Colored Coins */ 43 | 44 | 'coloredCoins.getIssueAssetTx': { 45 | 'params': ['params'], 46 | 'callback': true 47 | }, 48 | 'coloredCoins.getSendAssetTx': { 49 | 'params': ['params'], 50 | 'callback': true 51 | }, 52 | 'coloredCoins.getAddressInfo': { 53 | 'params': ['address'], 54 | 'callback': true 55 | }, 56 | 'coloredCoins.getStakeHolders': { 57 | 'params': ['assetId'], 58 | 'optional': ['numConfirmations'], 59 | 'callback': true 60 | }, 61 | 'coloredCoins.getAssetData': { 62 | 'params': ['params'], 63 | 'callback': true 64 | }, 65 | 'coloredCoins.verifyIssuer': { 66 | 'params': ['assetId', 'json'], 67 | 'callback': true 68 | }, 69 | 'coloredCoins.broadcastTx': { 70 | 'params': ['params'], 71 | 'callback': true 72 | }, 73 | 'coloredCoins.signTx': { 74 | 'params': ['unsignedTx', 'privateKey'] 75 | }, 76 | 77 | /* HD Wallet */ 78 | 79 | 'hdwallet.getAddresses': { 80 | 'callback': true 81 | }, 82 | 'hdwallet.getAddressPrivateKey': { 83 | 'params': ['address'], 84 | 'callback': true 85 | }, 86 | 'hdwallet.getAddressPath': { 87 | 'params': ['address'], 88 | 'callback': true 89 | }, 90 | 'hdwallet.getPrivateSeed': { 91 | }, 92 | 'hdwallet.getPrivateKey': { 93 | 'optional': ['account', 'addressIndex'] 94 | }, 95 | 'hdwallet.getPublicKey': { 96 | 'optional': ['account', 'addressIndex'] 97 | }, 98 | 'hdwallet.getAddress': { 99 | 'optional': ['account', 'addressIndex'] 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /bin/run.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | var path = require('path-extra') 3 | var express = require('express') 4 | var auth = require('basic-auth') 5 | // var mkpath = require('mkpath') 6 | var Colu = require('../src/colu') 7 | // var jf = require('jsonfile') 8 | // var hash = require('crypto-hashing') 9 | var morgan = require('morgan')('dev') 10 | var methods = require('./methods') 11 | var jsonrpc = require('node-express-json-rpc2-async') 12 | var fs = require('fs') 13 | var http = require('http') 14 | var https = require('https') 15 | 16 | var settings 17 | var privateKey 18 | var certificate 19 | var sslCredentials 20 | var serverSettingsPath = path.join(path.datadir('colu'), 'settings') 21 | 22 | try { 23 | settings = require(serverSettingsPath) 24 | } catch (e) {} 25 | 26 | settings = settings || {} 27 | settings.colu = settings.colu || {} 28 | settings.colu.network = settings.colu.network || process.env.COLU_SDK_NETWORK // Optional: Possible values are testnet and mainnet. Default value is mainnet 29 | settings.colu.coluHost = settings.colu.coluHost || process.env.COLU_SDK_COLU_HOST // Optional 30 | settings.colu.apiKey = settings.colu.apiKey || process.env.COLU_SDK_API_KEY // Mandatory in case COLU_SDK_NETWORK is mainnet 31 | settings.colu.privateSeed = settings.colu.privateSeed || process.env.COLU_SDK_PRIVATE_SEED // Optional (otherwise generate new one every server start) 32 | settings.colu.privateSeedWIF = settings.colu.privateSeedWIF || process.env.COLU_SDK_PRIVATE_SEED_WIF // Optional (otherwise generate new one every server start) 33 | // Note: only one of COLU_SDK_PRIVATE_SEED and COLU_SDK_PRIVATE_SEED_WIF should be filled 34 | settings.colu.redisPort = settings.colu.redisPort || process.env.COLU_SDK_REDIS_PORT // Optional 35 | settings.colu.redisHost = settings.colu.redisHost || process.env.COLU_SDK_REDIS_HOST // Optional 36 | 37 | settings.server = settings.server || {} 38 | 39 | settings.server.httpPort = settings.server.httpPort || process.env.COLU_SDK_RPC_SERVER_HTTP_PORT || process.env.PORT || 80 // Optional 40 | settings.server.httpsPort = settings.server.httpsPort || process.env.COLU_SDK_RPC_SERVER_HTTPS_PORT || 443 // Optional 41 | settings.server.host = settings.server.host || process.env.COLU_SDK_RPC_SERVER_HOST || '0.0.0.0' // Optional 42 | 43 | settings.server.usessl = settings.server.usessl || (process.env.COLU_SDK_RPC_USE_SSL === 'true') // Optional 44 | settings.server.useBoth = settings.server.useBoth || (process.env.COLU_SDK_RPC_USE_BOTH === 'true') // both HTTP and HTTPS - Optional 45 | settings.server.privateKeyPath = settings.server.privateKeyPath || process.env.COLU_SDK_RPC_PRIVATE_KEY_PATH // Mandatory in case COLU_SDK_RPC_USE_SSL or COLU_SDK_RPC_USE_BOTH is true 46 | settings.server.certificatePath = settings.server.certificatePath || process.env.COLU_SDK_RPC_CERTIFICATE_PATH // Mandatory in case COLU_SDK_RPC_USE_SSL or COLU_SDK_RPC_USE_BOTH is true 47 | 48 | settings.server.useBasicAuth = settings.server.useBasicAuth || (process.env.COLU_SDK_RPC_USE_BASIC_AUTH === 'true') // Optional 49 | settings.server.userName = settings.server.userName || process.env.COLU_SDK_RPC_USER_NAME // Manadatory in case COLU_SDK_RPC_USE_BASIC_AUTH is true 50 | settings.server.password = settings.server.password || process.env.COLU_SDK_RPC_PASSWORD // Manadatory in case COLU_SDK_RPC_USE_BASIC_AUTH is true 51 | 52 | if (settings.server.usessl && settings.server.privateKeyPath && settings.server.certificatePath) { 53 | try { 54 | privateKey = fs.readFileSync(settings.server.privateKeyPath, 'utf8') 55 | certificate = fs.readFileSync(settings.server.certificatePath, 'utf8') 56 | sslCredentials = {key: privateKey, cert: certificate} 57 | } catch (e) {} 58 | } 59 | 60 | var colu = new Colu(settings.colu) 61 | var app = express() 62 | 63 | app.use(morgan) 64 | 65 | app.use(function (req, res, next) { 66 | res.header('Access-Control-Allow-Origin', '*') 67 | res.header('Access-Control-Allow-Methods', 'POST') 68 | res.header('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept') 69 | next() 70 | }) 71 | 72 | if (settings.server.useBasicAuth && settings.server.userName && settings.server.password) { 73 | app.use(function (req, res, next) { 74 | var basicAuthCredentials = auth(req) 75 | if (!basicAuthCredentials || basicAuthCredentials.name !== settings.server.userName || basicAuthCredentials.pass !== settings.server.password) { 76 | res.statusCode = 401 77 | res.setHeader('WWW-Authenticate', 'Basic realm=""') 78 | res.end('Access denied') 79 | } else { 80 | next() 81 | } 82 | }) 83 | } 84 | 85 | app.use(jsonrpc()) 86 | 87 | app.options('/', function (req, res, next) { 88 | res.status(200).end() 89 | }) 90 | 91 | app.post('/', function (req, res, next) { 92 | if (!req.body) return 93 | 94 | res.rpc(req.body.method, function (params, respond) { 95 | var methodObj = methods[req.body.method] 96 | if (!methodObj) { 97 | return respond({error: { 98 | code: jsonrpc.METHOD_NOT_FOUND, 99 | message: 'Method not found', 100 | data: {missingMethod: req.body.method} 101 | }}) 102 | } 103 | 104 | /* prepare the parameters */ 105 | var orderedParams 106 | var requiredParamNames = methods[req.body.method].params 107 | if (!requiredParamNames) { 108 | // no parameters 109 | orderedParams = [] 110 | } else if (requiredParamNames.length === 1 && requiredParamNames[0] === 'params') { 111 | // use the params as is 112 | orderedParams = [params] 113 | } else { 114 | orderedParams = [] 115 | var paramName 116 | var i 117 | for (i = 0; i < requiredParamNames.length; i++) { 118 | paramName = requiredParamNames[i] 119 | if (!params || !params[paramName]) { 120 | return respond({error: { 121 | code: jsonrpc.INVALID_PARAMS, 122 | message: 'Invalid params', 123 | data: 'required parameters: ' + requiredParamNames.toString() + ', given: ' + (!params ? params : Object.keys(params)) + '.' 124 | }}) 125 | } 126 | 127 | orderedParams.push(params[paramName]) 128 | } 129 | } 130 | 131 | var optional = methods[req.body.method].optional 132 | if (optional && params) { 133 | for (i = 0; i < optional.length; i++) { 134 | //now without throwing an error... 135 | paramName = optional[i] 136 | if (params[paramName]) { 137 | orderedParams.push(params[paramName]) 138 | } 139 | } 140 | } 141 | 142 | /* call the method (with or without callback) */ 143 | methodObj = getMethodObj(req.body.method) 144 | if (!methods[req.body.method].callback) { 145 | var result = methodObj.method.apply(methodObj.thisObj, orderedParams) 146 | return respond({result: getFormattedValue(result)}) 147 | } 148 | // push callback to be last argument 149 | var callback = function (err, result) { 150 | if (err) { 151 | return respond({ 152 | error: { 153 | code: jsonrpc.INTERNAL_ERROR, 154 | message: 'Internal error', 155 | data: err 156 | } 157 | }) 158 | } 159 | respond({result: getFormattedValue(result)}) 160 | } 161 | orderedParams.push(callback) 162 | methodObj.method.apply(methodObj.thisObj, orderedParams) 163 | }) 164 | }) 165 | 166 | app.use(function (req, res, next) { 167 | res.status(404) 168 | if (req.accepts('json')) return res.send({ error: 'Not found' }) 169 | res.type('txt').send('Not found') 170 | }) 171 | 172 | colu.once('connect', function () { 173 | if (sslCredentials) { 174 | launchServer('https', sslCredentials) 175 | 176 | if (settings.server.useBoth) { 177 | launchServer('http') 178 | } 179 | } else { 180 | launchServer('http') 181 | } 182 | }) 183 | 184 | colu.once('error', function (err) { 185 | console.error(err) 186 | process.exit(-1) 187 | }) 188 | 189 | colu.init() 190 | 191 | var getMethodObj = function (methodName) { 192 | var methodParts = methodName.split('.') 193 | var thisObj = colu 194 | var method = colu[methodParts[0]] 195 | for (var i = 1; i < methodParts.length; i++) { 196 | thisObj = method 197 | method = thisObj[methodParts[i]] 198 | } 199 | return { 200 | method: method, 201 | thisObj: thisObj 202 | } 203 | } 204 | 205 | // sslCredentials - relevant if and only if (type === 'http') 206 | var launchServer = function (type, sslCredentials) { 207 | var server = (type === 'https') ? https.createServer(sslCredentials, app) : http.createServer(app) 208 | var port = (type === 'https') ? settings.server.httpsPort : settings.server.httpPort 209 | server.listen(port, settings.server.host, function () { 210 | console.log(type + ' server started on port', port) 211 | app.emit('connect', type) 212 | }) 213 | server.on('error', function (err) { 214 | console.error('err = ', err) 215 | process.exit(-1) 216 | }) 217 | } 218 | 219 | // convert the given result to suitable output to be given as a response 220 | var getFormattedValue = function (result) { 221 | if (result && (typeof result.getFormattedValue === 'function')) { 222 | return result.getFormattedValue() 223 | } 224 | return result 225 | } 226 | 227 | module.exports = app 228 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp') 2 | var source = require('vinyl-source-stream') 3 | var buffer = require('vinyl-buffer') 4 | var browserify = require('browserify') 5 | var babel = require('gulp-babel') 6 | 7 | gulp.task('browserify', function () { 8 | return browserify({ 9 | entries: 'src/colu.js', 10 | insertGlobals: true, 11 | standalone: 'Colu', 12 | ignoreMissing: true, 13 | // Define FormData globally to prevent fallback to browser's FormData and thus fix IE crash 14 | insertGlobalVars: { 15 | FormData: function (file, dir) { 16 | return 'require("form-data")' 17 | } 18 | }, 19 | debug: true 20 | }) 21 | .ignore('redis-parser') 22 | .ignore('mkpath') 23 | .bundle() 24 | .pipe(source('colu.client.js')) 25 | .pipe(buffer()) 26 | // .pipe(uglify({ mangle: false })) 27 | .pipe(gulp.dest('client')) 28 | }) 29 | 30 | gulp.task('babel', function () { 31 | return gulp.src('client/colu.client.js') 32 | .pipe(babel({ 33 | presets: ['es2015'] 34 | })) 35 | .pipe(gulp.dest('client')) 36 | }) 37 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "colu", 3 | "version": "0.11.3", 4 | "description": "Colu SDK for engine and colored-coins api.", 5 | "main": "src/colu.js", 6 | "scripts": { 7 | "start": "node bin/run.js", 8 | "test": "cross-env COLU_SDK_NETWORK=testnet mocha", 9 | "coveralls": "cat ./coverage/lcov.info | coveralls", 10 | "browserify": "gulp browserify", 11 | "uglify": "uglifyjs -c -o ./client/colu.client.min.js -- ./client/colu.client.js", 12 | "babel": "gulp babel", 13 | "client_commit": "git commit --quiet ./client -m \"browserify and uglify\" || echo \"No changes to commit\"", 14 | "preversion": "npm run browserify && npm run babel && npm run uglify && npm run client_commit" 15 | }, 16 | "bin": { 17 | "colu": "bin/run.js" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "https://github.com/Colu-platform/colu-nodejs.git" 22 | }, 23 | "keywords": [ 24 | "colu", 25 | "engine", 26 | "colored", 27 | "coins", 28 | "bitcoin" 29 | ], 30 | "contributors": [ 31 | { 32 | "name": "Tal Beja", 33 | "email": "tal@colu.com" 34 | }, 35 | { 36 | "name": "Eliran Zach", 37 | "email": "thehobbit85@gmail.com" 38 | }, 39 | { 40 | "name": "Oded Leiba", 41 | "email": "oded@colu.com" 42 | } 43 | ], 44 | "license": "https://www.colu.co/TOS", 45 | "bugs": { 46 | "url": "https://github.com/Colu-platform/colu-nodejs/issues" 47 | }, 48 | "homepage": "https://github.com/Colu-platform/colu-nodejs", 49 | "dependencies": { 50 | "assert": "^1.3.0", 51 | "async": "^1.3.0", 52 | "basic-auth": "^1.0.3", 53 | "body-parser": "^1.13.3", 54 | "coloredcoins-sdk": "^0.1.3", 55 | "crypto-hashing": "^0.3.1", 56 | "express": "^4.14.1", 57 | "jsonfile": "^2.2.1", 58 | "mkpath": "^1.0.0", 59 | "morgan": "^1.6.1", 60 | "node-express-json-rpc2-async": "^1.0.2", 61 | "path-extra": "^1.0.3", 62 | "request": "2.69.0", 63 | "should": "^8.1.1", 64 | "socket.io-client": "^1.3.7", 65 | "supertest": "^1.1.0" 66 | }, 67 | "devDependencies": { 68 | "babel-preset-es2015": "^6.22.0", 69 | "browserify": "14.0.0", 70 | "chai": "^3.0.0", 71 | "cross-env": "^3.1.4", 72 | "gulp": "^3.9.1", 73 | "gulp-babel": "^6.1.2", 74 | "istanbul": "^0.3.22", 75 | "lodash": "^4.16.6", 76 | "mocha": "^2.2.4", 77 | "portfinder": "^0.4.0", 78 | "vinyl-buffer": "^1.0.0", 79 | "vinyl-source-stream": "^1.1.0" 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/colu.js: -------------------------------------------------------------------------------- 1 | var util = require('util') 2 | var events = require('events') 3 | var async = require('async') 4 | var request = require('request') 5 | 6 | var ColoredCoins = require('coloredcoins-sdk') 7 | 8 | var mainnetColuHost = 'https://engine.colu.co' 9 | var testnetColuHost = 'https://testnet-engine.colu.co' 10 | 11 | var sendingMethods = ['address', 'phone_number', 'phoneNumber', 'facebook', 'facebookId', 'email', 'user_id', 'userId'] 12 | 13 | var Colu = function (settings) { 14 | var self = this 15 | settings = settings || {} 16 | if (settings.network === 'testnet') { 17 | settings.coluHost = self.coluHost = settings.coluHost || testnetColuHost 18 | self.network_name = 'testnet' 19 | } else { 20 | settings.coluHost = self.coluHost = settings.coluHost || mainnetColuHost 21 | self.network_name = 'mainnet' 22 | } 23 | self.apiKey = settings.apiKey 24 | self.coloredCoins = new ColoredCoins(settings) 25 | self.hdwallet = self.coloredCoins.hdwallet 26 | } 27 | 28 | util.inherits(Colu, events.EventEmitter) 29 | 30 | Colu.encryptPrivateKey = ColoredCoins.encryptPrivateKey 31 | Colu.decryptPrivateKey = ColoredCoins.decryptPrivateKey 32 | Colu.createNewKey = ColoredCoins.createNewKey 33 | 34 | Colu.prototype.init = function (cb) { 35 | var self = this 36 | 37 | function handleError (err) { 38 | self.emit('error') 39 | if (cb) return cb(err) 40 | } 41 | 42 | self.coloredCoins.init(function (err) { 43 | if (err) return handleError(err) 44 | self.emit('connect') 45 | if (cb) cb() 46 | }) 47 | } 48 | 49 | Colu.prototype.buildTransaction = function (type, args, cb) { 50 | var dataParams = { 51 | cc_args: args 52 | } 53 | var path = this.coluHost + '/build_finance_' + type 54 | var apiKey = args.token || this.apiKey 55 | if (this.network_name === 'mainnet' && !apiKey) { 56 | return cb('Must have apiKey/Token and/or set network to testnet.') 57 | } 58 | if (apiKey) { 59 | dataParams.token = apiKey 60 | } 61 | request.post(path, {json: dataParams}, function (err, response, body) { 62 | if (err) return cb(err) 63 | if (!response || response.statusCode !== 200) return cb(body) 64 | cb(null, body) 65 | }) 66 | } 67 | 68 | Colu.prototype.signAndTransmit = function (assetInfo, callback) { 69 | var self = this 70 | async.waterfall([ 71 | function (cb) { 72 | self.sign(assetInfo.txHex, cb) 73 | }, 74 | function (signedTxHex, cb) { 75 | self.transmit(signedTxHex, assetInfo.financeTxid, cb) 76 | } 77 | ], 78 | function (err, resp) { 79 | if (err) return callback(err) 80 | if (!resp.txid2 || !resp.txid2.txid) { 81 | return callback('Unexpected response from Colu engine') 82 | } 83 | assetInfo.txid = resp.txid2.txid 84 | callback(null, assetInfo) 85 | }) 86 | } 87 | 88 | Colu.prototype.sign = function (txHex, callback) { 89 | this.hdwallet.sign(txHex, callback) 90 | } 91 | 92 | Colu.prototype.transmit = function (signedTxHex, lastTxid, callback) { 93 | var self = this 94 | var dataParams = { 95 | last_txid: lastTxid, 96 | tx_hex: signedTxHex 97 | } 98 | var path = this.coluHost + '/transmit_financed' 99 | request.post(path, { json: dataParams }, function (err, response, body) { 100 | if (err) return callback(err) 101 | if (!response || response.statusCode !== 200) { 102 | return callback(body) 103 | } 104 | callback(null, body) 105 | }) 106 | } 107 | 108 | Colu.prototype.issueAsset = function (args, callback) { 109 | var self = this 110 | 111 | var privateKey 112 | var publicKey 113 | var receivingAddresses 114 | var financeTxid 115 | var transmit = args.transmit !== false 116 | args.transfer = args.transfer || [] 117 | var hdwallet = self.hdwallet 118 | 119 | async.waterfall([ 120 | // Build finance transaction. 121 | function (cb) { 122 | if (!args.issueAddress) return cb(null, hdwallet.getPrivateKey(args.accountIndex)) 123 | hdwallet.getAddressPrivateKey(args.issueAddress, cb) 124 | }, 125 | function (priv, cb) { 126 | privateKey = priv 127 | publicKey = privateKey.pub 128 | args.issueAddress = args.issueAddress || publicKey.getAddress(self.hdwallet.network).toString() 129 | 130 | var sendingAmount = parseInt(args.amount, 10) 131 | args.transfer.forEach(function (to) { 132 | var sendingMethod = null 133 | Object.keys(to).forEach(function (key) { 134 | if (~sendingMethods.indexOf(key)) { 135 | sendingMethod = key 136 | } 137 | }) 138 | if (!sendingMethod) { 139 | to.address = args.issueAddress 140 | } 141 | sendingAmount -= parseInt(to.amount, 10) 142 | }) 143 | if (sendingAmount > 0) { 144 | args.transfer.push({ 145 | address: args.issueAddress, 146 | amount: sendingAmount 147 | }) 148 | } 149 | 150 | receivingAddresses = args.transfer 151 | args.flags = args.flags || {} 152 | args.flags.injectPreviousOutput = true 153 | self.buildTransaction('issue', args, cb) 154 | }, 155 | function (assetInfo, cb) { 156 | if (typeof assetInfo === 'function') return assetInfo('wrong server response') 157 | if (!assetInfo || !assetInfo.txHex) return cb('wrong server response') 158 | financeTxid = assetInfo.financeTxid 159 | if (!transmit) { 160 | return self.sign(assetInfo.txHex, cb) 161 | } 162 | self.signAndTransmit(assetInfo, cb) 163 | }, 164 | function (res, cb) { 165 | if (!transmit) { 166 | return cb(null, {financeTxid: financeTxid, signedTxHex: res}) 167 | } 168 | res.receivingAddresses = receivingAddresses 169 | res.issueAddress = args.issueAddress 170 | cb(null, res) 171 | } 172 | ], 173 | callback) 174 | } 175 | 176 | Colu.prototype.sendAsset = function (args, callback) { 177 | var self = this 178 | var transmit = args.transmit !== false 179 | var financeTxid 180 | async.waterfall([ 181 | // Build finance transaction. 182 | function (cb) { 183 | if ((!args.from || !Array.isArray(args.from) || !args.from.length) && (!args.sendutxo || !Array.isArray(args.sendutxo) || !args.sendutxo.length)) { 184 | return cb('Should have from as array of addresses or sendutxo as array of utxos.') 185 | } 186 | args.flags = args.flags || {} 187 | args.flags.injectPreviousOutput = true 188 | self.buildTransaction('send', args, cb) 189 | }, 190 | function (assetInfo, cb) { 191 | financeTxid = assetInfo.financeTxid 192 | if (!transmit) { 193 | return self.sign(assetInfo.txHex, cb) 194 | } 195 | self.signAndTransmit(assetInfo, cb) 196 | }, 197 | function (res, cb) { 198 | if (!transmit) { 199 | return cb(null, {financeTxid: financeTxid, signedTxHex: res}) 200 | } 201 | cb(null, res) 202 | } 203 | ], 204 | callback) 205 | } 206 | 207 | Colu.prototype.burnAsset = function (args, callback) { 208 | var self = this 209 | var transmit = args.transmit !== false 210 | var financeTxid 211 | args.transfer = args.transfer || [] 212 | 213 | async.waterfall([ 214 | // Build finance transaction. 215 | function (cb) { 216 | if ((!args.from || !Array.isArray(args.from) || !args.from.length) && (!args.sendutxo || !Array.isArray(args.sendutxo) || !args.sendutxo.length)) { 217 | return cb('Should have from as array of addresses or sendutxo as array of utxos.') 218 | } 219 | args.transfer.forEach(function (to) { 220 | var sendingMethod = null 221 | Object.keys(to).forEach(function (key) { 222 | if (~sendingMethods.indexOf(key)) { 223 | sendingMethod = key 224 | } 225 | }) 226 | if (!sendingMethod) { 227 | return cb('Received invalid transfer element: can\'t find who to send to') 228 | } 229 | }) 230 | args.flags = args.flags || {} 231 | args.flags.injectPreviousOutput = true 232 | self.buildTransaction('burn', args, cb) 233 | }, 234 | function (assetInfo, cb) { 235 | financeTxid = assetInfo.financeTxid 236 | if (!transmit) { 237 | return self.sign(assetInfo.txHex, cb) 238 | } 239 | self.signAndTransmit(assetInfo, cb) 240 | }, 241 | function (res, cb) { 242 | if (!transmit) { 243 | return cb(null, {financeTxid: financeTxid, signedTxHex: res}) 244 | } 245 | cb(null, res) 246 | } 247 | ], 248 | callback) 249 | } 250 | 251 | Colu.prototype.getAssets = function (callback) { 252 | this.coloredCoins.getAssets(callback) 253 | } 254 | 255 | Colu.prototype.getTransactions = function (addresses, callback) { 256 | this.coloredCoins.getTransactions(addresses, callback) 257 | } 258 | 259 | Colu.prototype.getTransactionsFromAddresses = function (addresses, callback) { 260 | this.coloredCoins.getTransactionsFromAddresses(addresses, callback) 261 | } 262 | 263 | Colu.prototype.getAssetMetadata = function (assetId, utxo, full, callback) { 264 | this.coloredCoins.getAssetMetadata(assetId, utxo, full, callback) 265 | } 266 | 267 | Colu.prototype.on = function (eventKey, callback) { 268 | this.coloredCoins.on(eventKey, callback) 269 | } 270 | 271 | Colu.prototype.onRevertedTransaction = function (callback) { 272 | this.coloredCoins.onRevertedTransaction(callback) 273 | } 274 | 275 | Colu.prototype.onRevertedCCTransaction = function (callback) { 276 | this.coloredCoins.onRevertedCCTransaction(callback) 277 | } 278 | 279 | Colu.prototype.onNewTransaction = function (callback) { 280 | this.coloredCoins.onNewTransaction(callback) 281 | } 282 | 283 | Colu.prototype.onNewCCTransaction = function (callback) { 284 | this.coloredCoins.onNewCCTransaction(callback) 285 | } 286 | 287 | Colu.prototype.getIssuedAssetsFromTransactions = function (addresses, transactions) { 288 | return this.coloredCoins.getIssuedAssetsFromTransactions(addresses, transactions) 289 | } 290 | 291 | Colu.prototype.getIssuedAssets = function (transactions, callback) { 292 | this.coloredCoins.getIssuedAssets(transactions, callback) 293 | } 294 | 295 | module.exports = Colu 296 | -------------------------------------------------------------------------------- /test/test-rpc.js: -------------------------------------------------------------------------------- 1 | var testUtils = require('./test-utils') 2 | var portfinder = require('portfinder') 3 | var chai = require('chai') 4 | var assert = require('chai').assert 5 | var expect = require('chai').expect 6 | var request = require('supertest'); 7 | var runningId = 1; 8 | 9 | describe('JSON-RPC API tests', function() { 10 | 11 | var url = 'http://localhost' 12 | var address 13 | var assetId 14 | var oldEnv 15 | var server 16 | 17 | before(function (done) { 18 | this.timeout(5000) 19 | portfinder.getPort(function (err, port) { 20 | assert.ifError(err) 21 | oldEnv = { 22 | port: process.env.COLU_SDK_RPC_SERVER_HTTP_PORT 23 | } 24 | process.env.COLU_SDK_RPC_SERVER_HTTP_PORT = port 25 | url = url + ':' + port 26 | server = require('../bin/run') 27 | server.on('connect', function (type) { 28 | if (type === 'http') { 29 | done() 30 | } 31 | }) 32 | }) 33 | }) 34 | 35 | after(function() { 36 | process.env.COLU_SDK_RPC_SERVER_HTTP_PORT = oldEnv.port 37 | }) 38 | 39 | it('Should return an \'Invalid request\' error.', function (done) { 40 | this.timeout(2000) 41 | var body = {key: 'value'} //Invalid JSON-RPC 2.0 body request 42 | request(url) 43 | .post('/') 44 | .send(body) 45 | .expect(200) 46 | .end(function (err, res) { 47 | if (err) return done(err) 48 | expect(res.body.jsonrpc).to.equal('2.0') 49 | expect(res.body.id).to.be.null 50 | expect(res.body.error).to.be.a('object') 51 | expect(res.body.error.code).to.equal(-32600) 52 | done() 53 | }) 54 | }) 55 | 56 | 57 | it('Should return a \'Method not found\' error.', function (done) { 58 | this.timeout(2000) 59 | var body = createJsonRpcRequestObj('nonExistingMethod') 60 | request(url) 61 | .post('/') 62 | .send(body) 63 | .expect(200) 64 | .end(function (err, res) { 65 | if (err) return done(err) 66 | expect(res.body.jsonrpc).to.equal('2.0') 67 | expect(res.body.id).to.equal(body.id) 68 | expect(res.body.error).to.be.a('object') 69 | expect(res.body.error.code).to.equal(-32601) 70 | expect(res.body.error.data.missingMethod).to.equal('nonExistingMethod') 71 | done() 72 | }) 73 | }) 74 | 75 | it('Should return an \'Invalid parameters\' error.', function (done) { 76 | this.timeout(2000) 77 | var body = createJsonRpcRequestObj('hdwallet.getAddressPath') 78 | request(url) 79 | .post('/') 80 | .send(body) 81 | .expect(200) 82 | .end(function (err, res) { 83 | if (err) return done(err) 84 | expect(res.body.jsonrpc).to.equal('2.0') 85 | expect(res.body.id).to.equal(body.id) 86 | expect(res.body.error).to.be.a('object') 87 | expect(res.body.error.code).to.equal(-32602) 88 | done() 89 | }) 90 | }) 91 | 92 | it('Should create and broadcast issue tx.', function (done) { 93 | this.timeout(20000) 94 | var params = testUtils.createIssueAssetArgs() 95 | var body = createJsonRpcRequestObj('issueAsset', params) 96 | request(url) 97 | .post('/') 98 | .send(body) 99 | .expect(200) 100 | .end(function (err, res) { 101 | if (err) return done(err) 102 | if (res.body.error) return done(res.body.error) 103 | expect(res.body.jsonrpc).to.equal('2.0') 104 | expect(res.body.id).to.equal(body.id) 105 | expect(res.body.result).to.be.a('object') 106 | testUtils.verifyIssueAssetResponse(res.body.result) 107 | address = res.body.result.issueAddress 108 | assetId = res.body.result.assetId 109 | done() 110 | }) 111 | }) 112 | 113 | it('Should return assets list for this wallet.', function (done) { 114 | this.timeout(10000) 115 | var body = createJsonRpcRequestObj('getAssets') 116 | request(url) 117 | .post('/') 118 | .send(body) 119 | .expect(200) 120 | .end(function (err, res) { 121 | if (err) return done(err) 122 | if (res.body.error) return done(res.body.error) 123 | expect(res.body.jsonrpc).to.equal('2.0') 124 | expect(res.body.id).to.equal(body.id) 125 | expect(res.body.result).to.be.a('array') 126 | expect(res.body.result).to.have.length.above(0) 127 | done() 128 | }) 129 | }) 130 | 131 | it('Should burn amount of assets from utxo.', function (done) { 132 | this.timeout(15000) 133 | var params = testUtils.createBurnAssetFromUtxoArgs() 134 | var body = createJsonRpcRequestObj('burnAsset', params) 135 | request(url) 136 | .post('/') 137 | .send(body) 138 | .expect(200) 139 | .end(function (err, res) { 140 | if (err) return done(err) 141 | if (res.body.error) return done(res.body.error) 142 | expect(res.body.jsonrpc).to.equal('2.0') 143 | expect(res.body.id).to.equal(body.id) 144 | testUtils.verifyBurnAssetResponse(res.body.result) 145 | done() 146 | }) 147 | }) 148 | 149 | it('Should burn amount of assets from address.', function (done) { 150 | this.timeout(15000) 151 | var params = testUtils.createBurnAssetFromAddressArgs() 152 | var body = createJsonRpcRequestObj('burnAsset', params) 153 | request(url) 154 | .post('/') 155 | .send(body) 156 | .expect(200) 157 | .end(function (err, res) { 158 | if (err) return done(err) 159 | if (res.body.error) return done(res.body.error) 160 | expect(res.body.jsonrpc).to.equal('2.0') 161 | expect(res.body.id).to.equal(body.id) 162 | testUtils.verifyBurnAssetResponse(res.body.result) 163 | done() 164 | }) 165 | }) 166 | 167 | it('Should create and broadcast send tx from utxo.', function (done) { 168 | this.timeout(15000) 169 | var params = testUtils.createSendAssetFromUtxoArgs() 170 | var body = createJsonRpcRequestObj('sendAsset', params) 171 | request(url) 172 | .post('/') 173 | .send(body) 174 | .expect(200) 175 | .end(function (err, res) { 176 | if (err) return done(err) 177 | if (res.body.error) return done(res.body.error) 178 | expect(res.body.jsonrpc).to.equal('2.0') 179 | expect(res.body.id).to.equal(body.id) 180 | testUtils.verifySendAssetResponse(res.body.result) 181 | done() 182 | }) 183 | }) 184 | 185 | it('Should create and broadcast send tx from address.', function (done) { 186 | this.timeout(10000) 187 | var params = testUtils.createSendAssetFromAddressArgs() 188 | var body = createJsonRpcRequestObj('sendAsset', params) 189 | request(url) 190 | .post('/') 191 | .send(body) 192 | .expect(200) 193 | .end(function (err, res) { 194 | if (err) return done(err) 195 | if (res.body.error) return done(res.body.error) 196 | expect(res.body.jsonrpc).to.equal('2.0') 197 | expect(res.body.id).to.equal(body.id) 198 | testUtils.verifySendAssetResponse(res.body.result) 199 | done() 200 | }) 201 | }) 202 | 203 | it('Should create and broadcast send tx to phone.', function (done) { 204 | this.timeout(15000) 205 | var params = testUtils.createSendAssetToPhoneArgs() 206 | var body = createJsonRpcRequestObj('sendAsset', params) 207 | request(url) 208 | .post('/') 209 | .send(body) 210 | .expect(200) 211 | .end(function (err, res) { 212 | if (err) return done(err) 213 | if (res.body.error) return done(res.body.error) 214 | expect(res.body.jsonrpc).to.equal('2.0') 215 | expect(res.body.id).to.equal(body.id) 216 | testUtils.verifySendAssetResponse(res.body.result) 217 | done() 218 | }) 219 | }) 220 | 221 | it('Should return transactions list for this wallet.', function (done) { 222 | this.timeout(10000) 223 | var body = createJsonRpcRequestObj('getTransactions') 224 | request(url) 225 | .post('/') 226 | .send(body) 227 | .expect(200) 228 | .end(function (err, res) { 229 | if (err) return done(err) 230 | if (res.body.error) return done(res.body.error) 231 | expect(res.body.jsonrpc).to.equal('2.0') 232 | expect(res.body.id).to.equal(body.id) 233 | expect(res.body.result).to.be.a('array') 234 | expect(res.body.result).to.have.length.above(0) 235 | done() 236 | }) 237 | }) 238 | 239 | it('Should return transactions list for a specific address.', function (done) { 240 | this.timeout(10000) 241 | var params = {addresses: [address]} 242 | var body = createJsonRpcRequestObj('getTransactions', params) 243 | request(url) 244 | .post('/') 245 | .send(body) 246 | .expect(200) 247 | .end(function (err, res) { 248 | if (err) return done(err) 249 | if (res.body.error) return done(res.body.error) 250 | expect(res.body.jsonrpc).to.equal('2.0') 251 | expect(res.body.id).to.equal(body.id) 252 | expect(res.body.result).to.be.a('array') 253 | expect(res.body.result).to.have.length.above(0) 254 | done() 255 | }) 256 | }) 257 | 258 | it('Should return issuances list for this wallet.', function (done) { 259 | this.timeout(15000) 260 | var body = createJsonRpcRequestObj('getIssuedAssets') 261 | request(url) 262 | .post('/') 263 | .send(body) 264 | .expect(200) 265 | .end(function (err, res) { 266 | if (err) return done(err) 267 | if (res.body.error) return done(res.body.error) 268 | expect(res.body.jsonrpc).to.equal('2.0') 269 | expect(res.body.id).to.equal(body.id) 270 | testUtils.verifyGetIssuedAssetsResponse(res.body.result) 271 | done() 272 | }) 273 | }) 274 | 275 | it('Should return asset metadata.', function (done) { 276 | this.timeout(10000) 277 | var params = testUtils.createGetAssetMetadataArgs() 278 | var body = createJsonRpcRequestObj('getAssetMetadata', params) 279 | request(url) 280 | .post('/') 281 | .send(body) 282 | .expect(200) 283 | .end(function (err, res) { 284 | if (err) return done(err) 285 | if (res.body.error) return done(res.body.error) 286 | expect(res.body.jsonrpc).to.equal('2.0') 287 | expect(res.body.id).to.equal(body.id) 288 | testUtils.verifyGetAssetMetadataResponse(res.body.result) 289 | done() 290 | }) 291 | }) 292 | 293 | it('Should get stakeholders.', function (done) { 294 | this.timeout(60000) 295 | var params = {assetId : assetId} 296 | var body = createJsonRpcRequestObj('coloredCoins.getStakeHolders', params) 297 | request(url) 298 | .post('/') 299 | .send(body) 300 | .expect(200) 301 | .end(function (err, res) { 302 | if (err) return done(err) 303 | if (res.body.error) return done(res.body.error) 304 | expect(res.body.jsonrpc).to.equal('2.0') 305 | expect(res.body.id).to.equal(body.id) 306 | expect(res.body.result.assetId).to.equal(assetId) 307 | expect(res.body.result.holders).to.be.a('array') 308 | expect(res.body.result.holders).to.have.length.above(0) 309 | done() 310 | }) 311 | }) 312 | 313 | it('Should return private seed.', function (done) { 314 | this.timeout(1000) 315 | var body = createJsonRpcRequestObj('hdwallet.getPrivateSeed') 316 | request(url) 317 | .post('/') 318 | .send(body) 319 | .expect(200) 320 | .end(function (err, res) { 321 | if (err) return done(err) 322 | if (res.body.error) return done(res.body.error) 323 | expect(res.body.jsonrpc).to.equal('2.0') 324 | expect(res.body.id).to.equal(body.id) 325 | expect(res.body.result).to.be.a('string') 326 | expect(res.body.result).to.have.length.above(0) 327 | done() 328 | }) 329 | }) 330 | 331 | it('Should return address path.', function (done) { 332 | this.timeout(2000) 333 | var params = {address : address} 334 | var body = createJsonRpcRequestObj('hdwallet.getAddressPath', params) 335 | request(url) 336 | .post('/') 337 | .send(body) 338 | .expect(200) 339 | .end(function (err, res) { 340 | if (err) return done(err) 341 | if (res.body.error) return done(res.body.error) 342 | expect(res.body.jsonrpc).to.equal('2.0') 343 | expect(res.body.id).to.equal(body.id) 344 | expect(res.body.result).to.be.a('string') 345 | expect(res.body.result).to.have.length.above(0) 346 | done() 347 | }) 348 | }) 349 | 350 | it('Should return a new valid address.', function (done) { 351 | this.timeout(2000) 352 | var body = createJsonRpcRequestObj('hdwallet.getAddress') 353 | request(url) 354 | .post('/') 355 | .send(body) 356 | .expect(200) 357 | .end(function (err, res) { 358 | if (err) return done(err) 359 | if (res.body.error) return done(res.body.error) 360 | expect(res.body.jsonrpc).to.equal('2.0') 361 | expect(res.body.id).to.equal(body.id) 362 | expect(res.body.result).to.be.a('string') 363 | expect(res.body.result).to.have.length.above(0) 364 | done() 365 | }) 366 | }) 367 | 368 | it('Should return a valid private key.', function (done) { 369 | this.timeout(2000) 370 | var body = createJsonRpcRequestObj('hdwallet.getPrivateKey') 371 | request(url) 372 | .post('/') 373 | .send(body) 374 | .expect(200) 375 | .end(function (err, res) { 376 | if (err) return done(err) 377 | if (res.body.error) return done(res.body.error) 378 | expect(res.body.jsonrpc).to.equal('2.0') 379 | expect(res.body.id).to.equal(body.id) 380 | expect(res.body.result).to.be.a('string') 381 | expect(res.body.result).to.have.length.above(0) 382 | done() 383 | }) 384 | }) 385 | 386 | it('Should return a valid private key from address.', function (done) { 387 | this.timeout(2000) 388 | var params = {address : address} 389 | var body = createJsonRpcRequestObj('hdwallet.getAddressPrivateKey', params) 390 | request(url) 391 | .post('/') 392 | .send(body) 393 | .expect(200) 394 | .end(function (err, res) { 395 | if (err) return done(err) 396 | if (res.body.error) return done(res.body.error) 397 | expect(res.body.jsonrpc).to.equal('2.0') 398 | expect(res.body.id).to.equal(body.id) 399 | expect(res.body.result).to.be.a('string') 400 | expect(res.body.result).to.have.length.above(0) 401 | done() 402 | }) 403 | }) 404 | 405 | it('Should return a valid public key.', function (done) { 406 | this.timeout(2000) 407 | var body = createJsonRpcRequestObj('hdwallet.getPublicKey') 408 | request(url) 409 | .post('/') 410 | .send(body) 411 | .expect(200) 412 | .end(function (err, res) { 413 | if (err) return done(err) 414 | if (res.body.error) return done(res.body.error) 415 | expect(res.body.jsonrpc).to.equal('2.0') 416 | expect(res.body.id).to.equal(body.id) 417 | expect(res.body.result).to.be.a('string') 418 | expect(res.body.result).to.have.length.above(0) 419 | done() 420 | }) 421 | }) 422 | 423 | }) 424 | 425 | var createJsonRpcRequestObj = function (methodName, params) { 426 | return { 427 | jsonrpc : '2.0', 428 | method: methodName, 429 | params: params, 430 | id: runningId++ 431 | } 432 | } 433 | -------------------------------------------------------------------------------- /test/test-utils.js: -------------------------------------------------------------------------------- 1 | var assert = require('chai').assert 2 | var expect = require('chai').expect 3 | 4 | var toAddress = 'mgNcWJp4hPd7MN6ets2P8HcB5k99aCs8cy' 5 | var assetId 6 | var fromAddress 7 | var phoneNumber = '+1234567890' 8 | var assetName = 'test_assetName' 9 | var issuer = 'test_issuer' 10 | var description = 'test_description' 11 | var icon = 'https://www.colu.co/layout/img/colu.png' 12 | var utxo 13 | 14 | var createIssueAssetArgs = function() { 15 | return { 16 | amount: 30, 17 | divisibility: 0, 18 | reissueable: false, 19 | transfer: [ 20 | { 21 | amount: 20 22 | } 23 | ], 24 | metadata: { 25 | assetName: assetName, 26 | issuer: issuer, 27 | description: description, 28 | urls: [{ 29 | name: 'icon', 30 | url: icon, 31 | mimeType: 'image/png' 32 | }] 33 | }, 34 | } 35 | } 36 | 37 | var verifyIssueAssetResponse = function(ans) { 38 | expect(ans.txHex).to.be.a('string') 39 | expect(ans.txHex).to.have.length.above(0) 40 | expect(ans.assetId).to.be.a('string') 41 | expect(ans.assetId).to.have.length.above(0) 42 | assetId = ans.assetId 43 | expect(ans.txid).to.be.a('string') 44 | expect(ans.txid).to.have.length.above(0) 45 | var issueTxid = ans.txid 46 | expect(ans.issueAddress).to.be.a('string') 47 | expect(ans.issueAddress).to.have.length.above(0) 48 | expect(ans.receivingAddresses).to.be.a('array') 49 | expect(ans.receivingAddresses).to.have.length.above(0) 50 | expect(ans.coloredOutputIndexes).to.be.a('array') 51 | expect(ans.coloredOutputIndexes).to.have.length.above(0) 52 | utxo = issueTxid + ':' + ans.coloredOutputIndexes[0] 53 | fromAddress = ans.receivingAddresses[0].address 54 | } 55 | 56 | var createBurnAssetFromUtxoArgs = function () { 57 | return { 58 | sendutxo: [utxo], 59 | transfer: [ 60 | { 61 | assetId: assetId, 62 | address: toAddress, 63 | amount: 10 64 | } 65 | ], 66 | burn: [ 67 | { 68 | assetId: assetId, 69 | amount: 1 70 | } 71 | ] 72 | } 73 | } 74 | 75 | var createBurnAssetFromAddressArgs = function () { 76 | return { 77 | from: [fromAddress], 78 | transfer: [ 79 | { 80 | assetId: assetId, 81 | address: fromAddress, 82 | amount: 5 83 | } 84 | ], 85 | burn: [ 86 | { 87 | assetId: assetId, 88 | amount: 1 89 | } 90 | ] 91 | } 92 | } 93 | 94 | var verifyBurnAssetResponse = function (ans) { 95 | expect(ans).to.be.a('object') 96 | expect(ans.txHex).to.be.a('string') 97 | expect(ans.txHex).to.have.length.above(0) 98 | expect(ans.txid).to.be.a('string') 99 | expect(ans.txid).to.have.length.above(0) 100 | expect(ans.coloredOutputIndexes).to.be.a('array') 101 | expect(ans.coloredOutputIndexes).to.have.length.above(0) 102 | utxo = ans.txid + ':' + ans.coloredOutputIndexes[0] 103 | } 104 | 105 | var createSendAssetFromUtxoArgs = function() { 106 | var address = fromAddress 107 | return { 108 | sendutxo: [utxo], 109 | to: [ 110 | { 111 | address: toAddress, 112 | assetId: assetId, 113 | amount: 3 114 | } 115 | ] 116 | } 117 | } 118 | 119 | var verifySendAssetResponse = function(ans) { 120 | expect(ans).to.be.a('object') 121 | expect(ans.txHex).to.be.a('string') 122 | expect(ans.txHex).to.have.length.above(0) 123 | expect(ans.txid).to.be.a('string') 124 | expect(ans.txid).to.have.length.above(0) 125 | } 126 | 127 | var createSendAssetFromAddressArgs = function() { 128 | var address = fromAddress 129 | return { 130 | from: [address], 131 | to: [ 132 | { 133 | address: toAddress, 134 | assetId: assetId, 135 | amount: 2 136 | } 137 | ] 138 | } 139 | } 140 | 141 | var createSendAssetToPhoneArgs = function() { 142 | var address = fromAddress 143 | return { 144 | from: [address], 145 | to: [ 146 | { 147 | phoneNumber: phoneNumber, 148 | assetId: assetId, 149 | amount: 1 150 | } 151 | ] 152 | } 153 | } 154 | 155 | var verifyGetIssuedAssetsResponse = function(issuances) { 156 | expect(issuances).to.be.a('array') 157 | expect(issuances).to.have.length.above(0) 158 | assert.equal(issuances[0].assetId, assetId) 159 | } 160 | 161 | var createGetAssetMetadataArgs = function() { 162 | return { 163 | assetId : assetId, 164 | utxo : utxo 165 | } 166 | } 167 | 168 | var verifyGetAssetMetadataResponse = function(metadata) { 169 | expect(metadata).to.be.a('object') 170 | assert.equal(metadata.assetName, assetName) 171 | assert.equal(metadata.issuer, issuer) 172 | assert.equal(metadata.description, description) 173 | assert.equal(metadata.icon, icon) 174 | } 175 | 176 | module.exports = { 177 | createIssueAssetArgs : createIssueAssetArgs, 178 | verifyIssueAssetResponse : verifyIssueAssetResponse, 179 | createBurnAssetFromUtxoArgs: createBurnAssetFromUtxoArgs, 180 | createBurnAssetFromAddressArgs: createBurnAssetFromAddressArgs, 181 | verifyBurnAssetResponse: verifyBurnAssetResponse, 182 | createSendAssetFromUtxoArgs : createSendAssetFromUtxoArgs, 183 | verifySendAssetResponse : verifySendAssetResponse, 184 | createSendAssetFromAddressArgs : createSendAssetFromAddressArgs, 185 | createSendAssetToPhoneArgs : createSendAssetToPhoneArgs, 186 | verifyGetIssuedAssetsResponse : verifyGetIssuedAssetsResponse, 187 | createGetAssetMetadataArgs : createGetAssetMetadataArgs, 188 | verifyGetAssetMetadataResponse : verifyGetAssetMetadataResponse 189 | } -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | var Colu = require('..') 3 | var testUtils = require('./test-utils') 4 | var expect = require('chai').expect 5 | var async = require('async') 6 | var _ = require('lodash') 7 | 8 | describe('Test Colu SDK', function () { 9 | 10 | var settings 11 | var colu 12 | var assetId 13 | 14 | before(function (done) { 15 | try { 16 | settings = require('./settings') 17 | } catch (e) { 18 | settings = { 19 | network: 'testnet', 20 | events: true, 21 | eventsSecure: true 22 | } 23 | } 24 | colu = new Colu(settings) 25 | colu.on('connect', done) 26 | colu.init() 27 | }) 28 | 29 | it('Should get an empty list of assets', function (done) { 30 | this.timeout(5000) 31 | colu.getAssets(function (err, assets) { 32 | if (err) return done(err) 33 | expect(assets).to.be.a('array') 34 | expect(assets).to.have.lengthOf(0) 35 | done() 36 | }) 37 | }) 38 | 39 | it('Should get an empty list of transactions', function (done) { 40 | this.timeout(5000) 41 | colu.getTransactions(function (err, transactions) { 42 | if (err) return done(err) 43 | expect(transactions).to.be.a('array') 44 | expect(transactions).to.have.lengthOf(0) 45 | done() 46 | }) 47 | }) 48 | 49 | it('Should create and broadcast issue tx.', function (done) { 50 | this.timeout(20000) 51 | var args = testUtils.createIssueAssetArgs() 52 | colu.issueAsset(args, function (err, ans) { 53 | if (err) return done(err) 54 | assetId = ans.assetId 55 | testUtils.verifyIssueAssetResponse(ans) 56 | done() 57 | }) 58 | }) 59 | 60 | it('Should return assets list for this wallet.', function (done) { 61 | this.timeout(20000) 62 | colu.getAssets(function (err, assets) { 63 | if (err) return done(err) 64 | expect(assets).to.be.a('array') 65 | expect(assets).to.have.length.above(0) 66 | done() 67 | }) 68 | }) 69 | 70 | it('Should burn amount of assets from utxo.', function (done) { 71 | this.timeout(100000) 72 | var args = testUtils.createBurnAssetFromUtxoArgs() 73 | var totalSupply 74 | async.waterfall([ 75 | function (cb) { 76 | colu.getAssetMetadata(assetId, null, cb) 77 | }, 78 | function (data, cb) { 79 | totalSupply = data.totalSupply 80 | colu.burnAsset(args, cb) 81 | }, 82 | function (data, cb) { 83 | testUtils.verifyBurnAssetResponse(data) 84 | colu.getAssetMetadata(assetId, null, cb) 85 | } 86 | ], 87 | function (err, data) { 88 | if (err) return done(err) 89 | expect(data.totalSupply).to.equal(totalSupply - _.sumBy(args.burn, 'amount')) 90 | done() 91 | }) 92 | }) 93 | 94 | it('Should burn amount of assets from address.', function (done) { 95 | this.timeout(20000) 96 | var args = testUtils.createBurnAssetFromAddressArgs() 97 | var totalSupply 98 | async.waterfall([ 99 | function (cb) { 100 | colu.getAssetMetadata(assetId, null, cb) 101 | }, 102 | function (data, cb) { 103 | totalSupply = data.totalSupply 104 | colu.burnAsset(args, cb) 105 | }, 106 | function (data, cb) { 107 | testUtils.verifyBurnAssetResponse(data) 108 | colu.getAssetMetadata(assetId, null, cb) 109 | } 110 | ], 111 | function (err, data) { 112 | if (err) return done(err) 113 | expect(data.totalSupply).to.equal(totalSupply - _.sumBy(args.burn, 'amount')) 114 | done() 115 | }) 116 | }) 117 | 118 | it('Should create and broadcast send tx from utxo.', function (done) { 119 | this.timeout(20000) 120 | var args = testUtils.createSendAssetFromUtxoArgs() 121 | colu.sendAsset(args, function (err, ans) { 122 | if (err) return done(err) 123 | testUtils.verifySendAssetResponse(ans) 124 | done() 125 | }) 126 | }) 127 | 128 | it('Should create and broadcast send tx from address.', function (done) { 129 | this.timeout(20000) 130 | var args = testUtils.createSendAssetFromAddressArgs() 131 | colu.sendAsset(args, function (err, ans) { 132 | if (err) return done(err) 133 | testUtils.verifySendAssetResponse(ans) 134 | done() 135 | }) 136 | }) 137 | 138 | it('Should create and broadcast send tx to phone.', function (done) { 139 | this.timeout(20000) 140 | var args = testUtils.createSendAssetToPhoneArgs() 141 | colu.sendAsset(args, function (err, ans) { 142 | if (err) return done(err) 143 | testUtils.verifySendAssetResponse(ans) 144 | done() 145 | }) 146 | }) 147 | 148 | it('Should return transactions list for this wallet.', function (done) { 149 | this.timeout(5000) 150 | colu.getTransactions(function (err, transactions) { 151 | if (err) return done(err) 152 | expect(transactions).to.be.a('array') 153 | expect(transactions).to.have.length.above(0) 154 | done() 155 | }) 156 | }) 157 | 158 | it('Should return issuances list for this wallet.', function (done) { 159 | this.timeout(5000) 160 | colu.getIssuedAssets(function (err, issuances) { 161 | if (err) return done(err) 162 | testUtils.verifyGetIssuedAssetsResponse(issuances) 163 | done() 164 | }) 165 | }) 166 | 167 | it('Should return asset metadata.', function (done) { 168 | this.timeout(10000) 169 | var args = testUtils.createGetAssetMetadataArgs() 170 | colu.getAssetMetadata(args.assetId, args.utxo, true, function (err, metadata) { 171 | if (err) return done(err) 172 | testUtils.verifyGetAssetMetadataResponse(metadata) 173 | done() 174 | }) 175 | }) 176 | 177 | it('Should return cached asset metadata.', function (done) { 178 | // this time with shorter default timeout 179 | var args = testUtils.createGetAssetMetadataArgs() 180 | colu.getAssetMetadata(args.assetId, args.utxo, false, function (err, metadata) { 181 | if (err) return done(err) 182 | testUtils.verifyGetAssetMetadataResponse(metadata) 183 | done() 184 | }) 185 | }) 186 | 187 | it('Should return new transaction secure.', function (done) { 188 | this.timeout(100000) 189 | colu.eventsSecure = true 190 | 191 | var txids = [] 192 | var txid 193 | var once = 0 194 | colu.on('newTransaction', function (transaction) { 195 | txids.push(transaction.txid) 196 | if (txid && ~txids.indexOf(txid) && !once++) return done() 197 | }) 198 | var args = testUtils.createIssueAssetArgs() 199 | colu.issueAsset(args, function (err, ans) { 200 | if (err) return done(err) 201 | txid = ans.txid 202 | if (txid && ~txids.indexOf(txid) && !once++) return done() 203 | }) 204 | }) 205 | 206 | it('Should return new transaction unsecure.', function (done) { 207 | this.timeout(100000) 208 | colu.eventsSecure = false 209 | 210 | var txids = [] 211 | var txid 212 | var once = 0 213 | colu.on('newTransaction', function (transaction) { 214 | txids.push(transaction.txid) 215 | if (txid && ~txids.indexOf(txid) && !once++) return done() 216 | }) 217 | var args = testUtils.createIssueAssetArgs() 218 | colu.issueAsset(args, function (err, ans) { 219 | if (err) return done(err) 220 | txid = ans.txid 221 | if (txid && ~txids.indexOf(txid) && !once++) return done() 222 | }) 223 | }) 224 | 225 | it('Should return new cc transaction secure.', function (done) { 226 | this.timeout(100000) 227 | 228 | colu.eventsSecure = true 229 | var txids = [] 230 | var txid 231 | var once = 0 232 | colu.on('newCCTransaction', function (transaction) { 233 | txids.push(transaction.txid) 234 | if (txid && ~txids.indexOf(txid) && !once++) return done() 235 | }) 236 | var args = testUtils.createIssueAssetArgs() 237 | colu.issueAsset(args, function (err, ans) { 238 | if (err) return done(err) 239 | txid = ans.txid 240 | if (txid && ~txids.indexOf(txid) && !once++) return done() 241 | }) 242 | }) 243 | 244 | it('Should return new cc transaction unsecure.', function (done) { 245 | this.timeout(100000) 246 | colu.eventsSecure = false 247 | 248 | var txids = [] 249 | var txid 250 | var once = 0 251 | colu.on('newCCTransaction', function (transaction) { 252 | txids.push(transaction.txid) 253 | if (txid && ~txids.indexOf(txid) && !once++) return done() 254 | }) 255 | var args = testUtils.createIssueAssetArgs() 256 | colu.issueAsset(args, function (err, ans) { 257 | if (err) return done(err) 258 | txid = ans.txid 259 | if (txid && ~txids.indexOf(txid) && !once++) return done() 260 | }) 261 | }) 262 | }) 263 | -------------------------------------------------------------------------------- /tsd.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "v4", 3 | "repo": "borisyankov/DefinitelyTyped", 4 | "ref": "master", 5 | "path": "typings", 6 | "bundle": "typings/tsd.d.ts", 7 | "installed": { 8 | "mime/mime.d.ts": { 9 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 10 | }, 11 | "express/express.d.ts": { 12 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 13 | }, 14 | "serve-static/serve-static.d.ts": { 15 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 16 | }, 17 | "node/node.d.ts": { 18 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 19 | }, 20 | "morgan/morgan.d.ts": { 21 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 22 | }, 23 | "basic-auth/basic-auth.d.ts": { 24 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 25 | }, 26 | "async/async.d.ts": { 27 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 28 | }, 29 | "form-data/form-data.d.ts": { 30 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 31 | }, 32 | "request/request.d.ts": { 33 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 34 | }, 35 | "mocha/mocha.d.ts": { 36 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 37 | }, 38 | "assertion-error/assertion-error.d.ts": { 39 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 40 | }, 41 | "chai/chai.d.ts": { 42 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 43 | }, 44 | "istanbul/istanbul.d.ts": { 45 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 46 | }, 47 | "browserify/browserify.d.ts": { 48 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 49 | }, 50 | "should/should.d.ts": { 51 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 52 | }, 53 | "socket.io-client/socket.io-client.d.ts": { 54 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 55 | }, 56 | "superagent/superagent.d.ts": { 57 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 58 | }, 59 | "supertest/supertest.d.ts": { 60 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 61 | }, 62 | "assert/assert.d.ts": { 63 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 64 | }, 65 | "source-map/source-map.d.ts": { 66 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 67 | }, 68 | "uglify-js/uglify-js.d.ts": { 69 | "commit": "99c4fb76b5d941939ba53e61d0daee4bd74f150f" 70 | } 71 | } 72 | } 73 | --------------------------------------------------------------------------------