项目作者: ArisenIO

项目描述 :
Official Node.js and Javascript API for interacting with ARISEN.
高级语言: JavaScript
项目地址: git://github.com/ArisenIO/arisenjsv1.git
创建时间: 2018-10-06T19:36:01Z
项目社区:https://github.com/ArisenIO/arisenjsv1

开源协议:

下载


Build Status
NPM

ArisenJSV1

General purpose library for Arisen blockchains.

Versions

ARISENIO/arisenjs Npm ARISENIO/arisen Docker Hub
tag: 1.0.7 npm install arisenjs (version 1) tag: v1.0.6 arisen/arisen:v1.0.6

Upgrade notes:

  • Converted some types in format module from unsigned to signed: UDecimalPad -> DecimalPad for example (15.0.1)
  • All asset and extended_asset amounts require exact decimal places (Change 1 RIX to 1.0000 RIX) (15.0.0)
  • Use config.verbose instead of config.debug (14.1.0)

Prior version matrix.

Usage

Ways to instantiate arisenjs.

  1. Arisen = require('arisenjs')
  2. // ARISEN account's Private Key or keys (array) provided statically or by way of a function.
  3. // For multiple keys, the get_required_keys API is used (more on that below).
  4. keyProvider: '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3'
  5. // Localhost Testnet (run ./docker/up.sh)
  6. arisen = Arisen({keyProvider})
  7. // Connect to a testnet or mainnet
  8. arisen = Arisen({httpEndpoint, chainId, keyProvider})
  9. // Cold-storage
  10. arisen = Arisen({httpEndpoint: null, chainId, keyProvider})
  11. // Read-only instance when 'arisenjs' is already a dependency
  12. arisen = Arisen.modules.api({/*config*/})
  13. // Read-only instance when an application never needs to write (smaller library)
  14. RsnApi = require('arisenjs-api')
  15. arisen = RsnApi({/*config*/})

No-arguments prints usage.

  1. arisen.getBlock()
  1. USAGE
  2. getBlock - Fetch a block from the ARISEN network.
  3. PARAMETERS
  4. {
  5. "block_num_or_id": "string"
  6. }

Start a aosd process. The docker in this repository provides a setup
the supports the examples in this README.

  1. cd ./docker && ./up.sh

All functions read only or transactional follow this pattern for parameters.

  1. // If the last argument is a function it is treated as a callback
  2. arisen.getBlock(1, (error, result) => {})
  3. // If a callback is not provided, a Promise is returned
  4. arisen.getBlock(1) // @returns {Promise}
  5. // Parameters can be positional or an object
  6. arisen.getBlock({block_num_or_id: 1})
  7. // An API with no parameters is invoked with an empty object or callback (avoids logging usage)
  8. arisen.getInfo({}) // @returns {Promise}
  9. arisen.getInfo((error, result) => { console.log(error, result) })

Chain and history API functions are available after creating the arisen object.
API methods and documentation are generated from the chain and history json files.

Until we generate a markdown for these, please convert the names in these
json to camel case functions.

  • "get_info": .. is arisen.getInfo(..)

Configuration

  1. Arisen = require('arisenjs')
  2. // Default configuration (additional options below)
  3. config = {
  4. chainId: null, // 32 byte (64 char) hex string
  5. keyProvider: ['PrivateKeys...'], // WIF string or array of keys..
  6. httpEndpoint: 'http://127.0.0.1:8888',
  7. expireInSeconds: 60,
  8. broadcast: true,
  9. verbose: false, // API activity
  10. sign: true
  11. }
  12. arisen = Arisen(config)
  • chainId hex - Unique ID for the ARISEN network you’re connecting too. This
    is required for valid transaction signing. The chainId is provided via the
    get_info API call.

    Identifies a chain by its initial genesis block. All transactions signed will
    only be valid the ARISEN network with this chainId. Verify the chainId for
    security reasons.

  • keyProvider [array<string>|string|function] - Provides ARISEN account’s Private Keys
    used to sign transaction. If multiple ARISEN account’s Private Keys are found, the API
    get_required_keys is called to discover which signing keys to use. If a
    function is provided, this function is called for Each wallet transaction.

  • httpEndpoint string - http or https location of a aosd server
    providing a chain API. When using arisenjs from a browser remember to configure
    the same origin policy in aosd or proxy server. For testing, aosd
    configuration access-control-allow-origin = * could be used.

    Set this value to null for a cold-storage (no network) configuration.

  • expireInSeconds number - number of seconds before the wallet transaction
    will expire. The time is based on the aosd’s clock. An unexpired
    transaction that may have had an error is a liability until the expiration
    is reached, this time should be brief.

  • broadcast [boolean=true] - post the wallet transaction to
    the ARISEN network. Use false to obtain a fully Signed ARISEN Transaction.

  • verbose [boolean=false] - verbose logging such as API activity.

  • debug [boolean=false] - low level debug logging (serialization).

  • sign [boolean=true] - sign the wallet transaction with a ARISEN account’s Private Key. Leaving
    a transaction unsigned avoids the need to provide a ARISEN account’s Private Key.

  • mockTransactions (advanced)

    • mockTransactions: () => null // 'pass', or 'fail'
    • pass - do not broadcast, always pretend that the wallet transaction worked
    • fail - do not broadcast, pretend the wallet transaction failed
    • null|undefined - broadcast as usual
  • transactionHeaders (advanced) - manually calculate transaction header. This
    may be provided so arisenjs does not need to make header related API calls to
    aos. Used in environments like cold-storage. This callback is called for
    every transaction. Headers are documented here arisenjs-api#headers.

    • transactionHeaders: (expireInSeconds, callback) => {callback(null/*error*/, headers)}
  • logger - default logging configuration.

    1. logger: {
    2. log: config.verbose ? console.log : null,
    3. error: console.error // null to disable
    4. }

    Turn off all error logging: config.logger = {error: null}

Options

Options may be provided after parameters.

  1. options = {
  2. authorization: 'alice@active',
  3. broadcast: true,
  4. sign: true
  5. }
  1. arisen.transfer('alice', 'bob', '1.0000 RIX', '', options)
  • authorization [array<auth>|auth] - identifies the
    signing account and permission typically in a multisig
    configuration. Authorization may be a string formatted as
    account@permission or an object<{actor: account, permission}>.

    • If missing default authorizations will be calculated.
    • If provided additional authorizations will not be added.
    • Performs deterministic sorting by account name

    If a default authorization is calculated the action’s 1st field must be
    an account_name. The account_name in the 1st field gets added as the
    active key authorization for the action.

  • broadcast [boolean=true] - post the wallet transaction to
    the ARISEN network. Use false to obtain a fully Signed ARISEN Transaction.

  • sign [boolean=true] - sign the wallet transaction with a ARISEN account’s Private Key. Leaving
    a transaction unsigned avoids the need to provide a ARISEN account’s Private Key.

Transaction

the wallet transaction function accepts the standard blockchain transaction.

Required transaction header fields will be added unless your signing without a
network connection (httpEndpoint == null). In that case provide you own headers:

  1. // only needed in cold-storage or for offline transactions
  2. const headers = {
  3. expiration: '2018-06-14T18:16:10'
  4. ref_block_num: 1,
  5. ref_block_prefix: 452435776
  6. }

Create and send (broadcast) a transaction:

  1. /** @return {Promise} */
  2. arisen.transaction(
  3. {
  4. // ...headers,
  5. actions: [
  6. {
  7. account: 'arisen.token',
  8. name: 'transfer',
  9. authorization: [{
  10. actor: 'inita',
  11. permission: 'active'
  12. }],
  13. data: {
  14. from: 'inita',
  15. to: 'initb',
  16. quantity: '7.0000 RIX',
  17. memo: ''
  18. }
  19. }
  20. ]
  21. }
  22. // options -- example: {broadcast: false}
  23. )

Named action functions

More concise functions are provided for applications that may use actions
more frequently. This avoids having lots of JSON in the code.

  1. // Run with no arguments to print usage.
  2. arisen.transfer()
  3. // Callback is last, when omitted a promise is returned
  4. arisen.transfer('inita', 'initb', '1.0000 RIX', '', (error, result) => {})
  5. arisen.transfer('inita', 'initb', '1.1000 RIX', '') // @returns {Promise}
  6. // positional parameters
  7. arisen.transfer('inita', 'initb', '1.2000 RIX', '')
  8. // named parameters
  9. arisen.transfer({from: 'inita', to: 'initb', quantity: '1.3000 RIX', memo: ''})
  10. // options appear after parameters
  11. options = {broadcast: true, sign: true}
  12. // `false` is a shortcut for {broadcast: false}
  13. arisen.transfer('inita', 'initb', '1.4000 RIX', '', false)

Read-write API methods and documentation are generated from the arisen
token and
system.

Assets amounts require zero padding. For a better user-experience, if you know
the correct precision you may use DecimalPad to add the padding.

  1. DecimalPad = Arisen.modules.format.DecimalPad
  2. userInput = '10.2'
  3. precision = 4
  4. assert.equal('10.2000', DecimalPad(userInput, precision))

For more advanced signing, see keyProvider and signProvider in
index.test.js.

Shorthand

Shorthand is available for some types such as Asset and Authority. This syntax
is only for concise functions and does not work when providing entire transaction
objects to arisen.transaction..

For example:

  • permission inita defaults inita@active
  • authority 'RSN6MRy..' expands {threshold: 1, keys: [key: 'RSN6MRy..', weight: 1]}
  • authority inita expands {{threshold: 1, accounts: [..actor: 'inita', permission: 'active', weight: 1]}}

New Account

New accounts will likely require some staked tokens for RAM and bandwidth.

  1. wif = '5KQwrPbwdL6PhXujxW37FSSQZ1JiwsST4cqQzDeyXtP79zkvFD3'
  2. pubkey = 'RSN6MRyAjQq8ud7hVNYcfnVPJqcVpscN5So8BhtHuGYqET5GDW5CV'
  3. arisen.transaction(tr => {
  4. tr.newaccount({
  5. creator: 'arisen',
  6. name: 'myaccount',
  7. owner: pubkey,
  8. active: pubkey
  9. })
  10. tr.buyrambytes({
  11. payer: 'arisen',
  12. receiver: 'myaccount',
  13. bytes: 8192
  14. })
  15. tr.delegatebw({
  16. from: 'arisen',
  17. receiver: 'myaccount',
  18. stake_net_quantity: '10.0000 RIX',
  19. stake_cpu_quantity: '10.0000 RIX',
  20. transfer: 0
  21. })
  22. })

Contract

Deploy and call smart contracts.

Compile

If you’re loading a wasm file, you do not need binaryen. If you’re loading
a wast file you can include and configure the binaryen compiler, this is
used to compile to wasm automatically when calling setcode.

Versions of binaryen may be problematic.

  1. $ npm install binaryen@37.0.0
  1. binaryen = require('binaryen')
  2. arisen = Arisen({keyProvider, binaryen})

Deploy

  1. wasm = fs.readFileSync(`docker/contracts/arisen.token/arisen.token.wasm`)
  2. abi = fs.readFileSync(`docker/contracts/arisen.token/arisen.token.abi`)
  3. // Publish contract to the ARISEN network
  4. arisen.setcode('myaccount', 0, 0, wasm) // @returns {Promise}
  5. arisen.setabi('myaccount', JSON.parse(abi)) // @returns {Promise}

Fetch a smart contract

  1. // @returns {Promise}
  2. arisen.contract('myaccount', [options], [callback])
  3. // Run immediately, `myaction` returns a Promise
  4. arisen.contract('myaccount').then(myaccount => myaccount.myaction(..))
  5. // Group actions. `transaction` returns a Promise but `myaction` does not
  6. arisen.transaction('myaccount', myaccount => { myaccount.myaction(..) })
  7. // Transaction with multiple contracts
  8. arisen.transaction(['myaccount', 'myaccount2'], ({myaccount, myaccount2}) => {
  9. myaccount.myaction(..)
  10. myaccount2.myaction(..)
  11. })

Offline or cold-storage contract

  1. arisen = Arisen({httpEndpoint: null})
  2. abi = fs.readFileSync(`docker/contracts/arisen.token/arisen.token.abi`)
  3. arisen.fc.abiCache.abi('myaccount', JSON.parse(abi))
  4. // Check that the ABI is available (print usage)
  5. arisen.contract('myaccount').then(myaccount => myaccount.create())

Offline or cold-storage transaction

  1. // ONLINE
  2. // Prepare headers
  3. expireInSeconds = 60 * 60 // 1 hour
  4. arisen = Arisen(/* {httpEndpoint: 'https://..'} */)
  5. info = await arisen.getInfo({})
  6. chainDate = new Date(info.head_block_time + 'Z')
  7. expiration = new Date(chainDate.getTime() + expireInSeconds * 1000)
  8. expiration = expiration.toISOString().split('.')[0]
  9. block = await arisen.getBlock(info.last_irreversible_block_num)
  10. transactionHeaders = {
  11. expiration,
  12. ref_block_num: info.last_irreversible_block_num & 0xFFFF,
  13. ref_block_prefix: block.ref_block_prefix
  14. }
  15. // OFFLINE (bring `transactionHeaders`)
  16. // All keys in keyProvider will sign.
  17. arisen = Arisen({httpEndpoint: null, chainId, keyProvider, transactionHeaders})
  18. transfer = await arisen.transfer('inita', 'initb', '1.0000 RIX', '')
  19. transferTransaction = transfer.transaction
  20. // ONLINE (bring `transferTransaction`)
  21. arisen = Arisen(/* {httpEndpoint: 'https://..'} */)
  22. processedTransaction = await arisen.pushTransaction(transferTransaction)

Custom Private Currency

  1. // more on the contract / transaction syntax
  2. await arisen.transaction('myaccount', myaccount => {
  3. // Create the initial token with its max supply
  4. // const options = {authorization: 'myaccount'} // default
  5. myaccount.create('myaccount', '10000000.000 TOK')//, options)
  6. // Issue some of the max supply for circulation into an arbitrary account
  7. myaccount.issue('myaccount', '10000.000 TOK', 'issue')
  8. })
  9. const balance = await arisen.getCurrencyBalance('myaccount', 'myaccount', 'TOK')
  10. console.log('Currency Balance', balance)

Calling Actions

Other ways to use contracts and transactions.

  1. // if either transfer fails, both will fail (1 transaction, 2 messages)
  2. await arisen.transaction(arisen =>
  3. {
  4. arisen.transfer('inita', 'initb', '1.0000 RIX', ''/*memo*/)
  5. arisen.transfer('inita', 'initc', '1.0000 RIX', ''/*memo*/)
  6. // Returning a promise is optional (but handled as expected)
  7. }
  8. // [options],
  9. // [callback]
  10. )
  11. // transaction on a single contract
  12. await arisen.transaction('myaccount', myaccount => {
  13. myaccount.transfer('myaccount', 'inita', '10.000 TOK@myaccount', '')
  14. })
  15. // mix contracts in the same transaction
  16. await arisen.transaction(['myaccount', 'arisen.token'], ({myaccount, arisen_token}) => {
  17. myaccount.transfer('inita', 'initb', '1.000 TOK@myaccount', '')
  18. arisen_token.transfer('inita', 'initb', '1.0000 RIX', '')
  19. })
  20. // The contract method does not take an array so must be called once for
  21. // each contract that is needed.
  22. const myaccount = await arisen.contract('myaccount')
  23. await myaccount.transfer('myaccount', 'inita', '1.000 TOK', '')
  24. // a transaction to a contract instance can specify multiple actions
  25. await myaccount.transaction(myaccountTr => {
  26. myaccountTr.transfer('inita', 'initb', '1.000 TOK', '')
  27. myaccountTr.transfer('initb', 'inita', '1.000 TOK', '')
  28. })

Development

From time-to-time the arisenjs and aos binary format will change between releases
so you may need to start aos with the --skip-transaction-signatures parameter
to get your transactions to pass.

Note, package.json has a “main” pointing to ./lib. The ./lib folder is for
es2015 code built in a separate step. If you’re changing and testing code,
import from ./src instead.

  1. Arisen = require('./src')
  2. // forceActionDataHex = false helps transaction readability but may trigger back-end bugs
  3. config = {verbose: true, debug: false, broadcast: true, forceActionDataHex: true, keyProvider}
  4. arisen = Arisen(config)

Fcbuffer

The arisen instance can provide serialization:

  1. // 'asset' is a type but could be any struct or type like: transaction or uint8
  2. type = {type: 1, data: '00ff'}
  3. buffer = arisen.fc.toBuffer('extensions_type', type)
  4. assert.deepEqual(type, arisen.fc.fromBuffer('extensions_type', buffer))
  5. // ABI Serialization
  6. arisen.contract('arisen.token', (error, arisen_token) => {
  7. create = {issuer: 'inita', maximum_supply: '1.0000 RIX'}
  8. buffer = arisen_token.fc.toBuffer('create', create)
  9. assert.deepEqual(create, arisen_token.fc.fromBuffer('create', buffer))
  10. })

Use Node v10+ for package-lock.json.

Related Libraries

These libraries are integrated into arisenjs seamlessly so you probably do not
need to use them directly. They are exported here giving more API access or
in some cases may be used standalone.

  1. var {format, api, ecc, json, Fcbuffer} = Arisen.modules
  • format ./format.md

    • Blockchain name validation
    • Asset string formatting
  • arisenjs-api [Github, NPM]

    • Remote API to an Arisen blockchain node (aos)
    • Use this library directly if you need read-only access to the ARISEN network
      (don’t need to sign transactions).
  • arisenjs-ecc [Github, NPM]

    • ARISEN account’s Private Key, Public Key, Signature, AES, Encryption / Decryption
    • Validate public or ARISEN account’s Private Keys
    • Encrypt or decrypt with Arisen compatible checksums
    • Calculate a shared secret
  • json {api, schema},

    • Blockchain definitions (api method names, blockchain schema)
  • arisenjs-keygen [Github, NPM]

    • ARISEN account’s Private Key storage and key management
  • Fcbuffer [Github, NPM]

    • Binary serialization used by the ARISEN network
    • Clients sign the binary form of the wallet transaction
    • Allows client to know what it is signing

Browser

  1. git clone https://github.com/ARISENIO/arisenjs.git
  2. cd arisenjs
  3. npm install
  4. npm run build_browser
  5. # builds: ./dist/arisen.js load with ./dist/index.html
  6. npm run build_browser_test
  7. # builds: ./dist/test.js run with ./dist/test.html
  1. <script src="arisen.js"></script>
  2. <script>
  3. var arisen = Arisen()
  4. //...
  5. </script>

Environment

Node and browser (es2015)