Skip to content

Commit

Permalink
Merge branch 'dev'
Browse files Browse the repository at this point in the history
  • Loading branch information
snowypowers committed Mar 20, 2018
2 parents 6223ba1 + 3c3573d commit a311721
Show file tree
Hide file tree
Showing 18 changed files with 460 additions and 68 deletions.
52 changes: 38 additions & 14 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,15 @@
defaults: &defaults
working_directory: ~/repo
docker:
- image: circleci/node:8.9.3
- image: circleci/node:8-stretch
version: 2
jobs:
build:
setup:
<<: *defaults
steps:
- run:
name: "Update npm"
command: "sudo npm i -g npm"
- run:
name: "Versions"
command: |
Expand All @@ -21,41 +24,62 @@ jobs:
- checkout
# Download and cache dependencies
- restore_cache:
key: neon-{{ checksum "package.json" }}
- run: npm install
keys:
- neon-{{ checksum "package-lock.json" }}
- neon-
- run: npm ci
- save_cache:
key: neon-{{ checksum "package.json" }}
key: neon-{{ checksum "package-lock.json" }}
paths:
- node_modules
integration-test:
build:
<<: *defaults
steps:
- checkout
- restore_cache:
key: neon-{{ checksum "package.json" }}
- run: npm run test:integration
keys:
- neon-{{ checksum "package-lock.json" }}
- neon-
- run: npm run prepublishOnly
unit-test:
<<: *defaults
steps:
- checkout
- restore_cache:
key: neon-{{ checksum "package.json" }}
keys:
- neon-{{ checksum "package-lock.json" }}
- neon-
- run: npm run test:unit
integration-test:
<<: *defaults
steps:
- checkout
- restore_cache:
keys:
- neon-{{ checksum "package-lock.json" }}
- neon-
- run: npm run test:integration

workflows:
version: 2
build_and_test:
jobs:
- build
- integration-test:
- setup
- build:
requires:
- build
- setup
filters:
branches:
only: master
ignore: gh-pages
- unit-test:
requires:
- build
- setup
filters:
branches:
ignore: gh-pages
- integration-test:
requires:
- setup
filters:
branches:
only: master
72 changes: 72 additions & 0 deletions docs/guides-adv_invokeconcepts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
id: adv_invokeconcepts
title: Advanced - Additional doInvoke functions
---

In this guide, we will discuss features of `doInvoke` that seem a little more complex. We will add an extra transaction to our invoke with `intents` and externalize the signing of our transaction - so we don't have to pass a user's private key to the config.

## Intents - adding extra transactions
In the previous `doInvoke` guide, we sent 1 GAS alongside our invocation as a fee. As described [here](http://docs.neo.org/en-us/sc/systemfees.html#smart-contract-fees), transactions with a cost under 10 GAS are essentially free.

So our `gas` field should stay 0 if your calculated fee remains below 10 GAS. You can determine this cost with an `invokeScript` RPC as we did [here](basic_createscript.html), evaluating the `gas_consumed` field in the response object.

There is a way to achieve this. We can add a transaction to ourselves as an intent, with a minimum amount of 0.00000001 GAS. This makes sure we don't have to send 1 GAS to our transaction (without that GAS being needed to consume the transaction), while making sure a transaction is registered and persisted to the blockchain.

The config object that `doInvoke` needs allows for a child object name intents. We set up our intents array as follows:
```js
import Neon, { CONST } from '@cityofzion/neon-js';

const intents = [
{
assetId: CONST.ASSET_ID.GAS,
value: 0.00000001,
scriptHash: Neon.get.scriptHashFromAddress(account.address)
}
];

// Add to config
config.intents = intents;

Neon.doInvoke(config).then(res => {
console.log(res);
});
```

## signingFunction
Right now we're adding a user's private key to the config object, which is sensitive information and should be handled carefully.
One way to do so is to externalize the signing of the transaction in a separate function. Right now, `signingFunction` is already used to sign with the Ledger.

Instead of sending a user's private key to the config object, we can send the public key and a function that will sign the transaction.
This function, the signingFunction, will receive the transaction and public key as parameters. Now, we can provide logic that retrieves the private key from our user - using the public key to do so - and signs the transaction when we retrieve the key.

> Do note that the signing function has to return a Promise!
```js
import Neon from '@cityofzion/neon-js';

function signTx(tx, publicKey) {
// Sign tx and attach signature onto tx
// The publicKey passed in is used as a check to ensure that the private and public keys match.

return new Promise(resolve =>
resolve(Neon.sign(tx, privateKey))
);
}

const config = {
net: "http://localhost:5000",
script: Neon.create.script({
scriptHash: '5b7074e873973a6ed3708862f219a6fbf4d1c411',
operation: 'balanceOf',
args: [Neon.u.reverseHex('cef0c0fdcfe7838eff6ff104f9cdec2922297537')]
}),
address: account.address,
publicKey: account.publicKey,
signingFunction: signTx
gas: 1
}

Neon.doInvoke(config).then(res => {
console.log(res);
});
```
87 changes: 87 additions & 0 deletions docs/guides-basic_invoke.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
---
id: basic_invoke
title: Basic - Invoking a Smart Contract
---

In this tutorial you will learn the difference between a testinvoke - as used in the Create Smart Contract Script guide - and an actual invocation of a Smart Contract. You will learn to use the `doInvoke` function to persist your calls to the NEO Blockchain.

This guide will continue on the previous guide, using the old [ICO Template](https://github.com/neo-project/examples-csharp/blob/master/ICO_Template/ICO_Template.cs).

## testinvoke vs invocationTransaction
In the previous guide, we used `Neon.rpc.Query.invokeScript` as an RPC call to invoke our script. What we've actually done is use the equivalent of neo-python's `testinvoke`, to literally test whether our invocation was successful.

Unless we turn this into an `invocationTransaction` RPC call, this will not persist to the blockchain. To do this, the script will be turned into a transaction. This, in turn, has to be signed with the user's private key.

## doInvoke
Luckily, `doInvoke` handles this for us! It will configure our script as a transaction, sign it with our private key and ultimately send it via an `invocationTransaction` RPC call.

To use the `doInvoke` function, we need the following minimal ingredients:
* `net`: 'MainNet', 'TestNet' or an API (like neoscan or neon-wallet-db)
* `script`: the Smart Contract script. You can use a VM script - created with `Neon.create.script` - or a ScriptParams object that looks like:

```js
{
scriptHash: '5b7074e873973a6ed3708862f219a6fbf4d1c411',
operation: 'balanceOf',
args: [Neon.u.reverseHex('cef0c0fdcfe7838eff6ff104f9cdec2922297537')]
}
```

* `account`: an `Account` object that has an address and private key stored. Alternatively, you can use `address` and `privateKey` directly to pass these values.
* `gas`: the gas fee we will attach to the transaction (this has to be an integer!)

This in turn will be stored in a configuration object

```js
const config = {
net: "http://localhost:5000",
script: Neon.create.script({
scriptHash: '5b7074e873973a6ed3708862f219a6fbf4d1c411',
operation: 'balanceOf',
args: [Neon.u.reverseHex('cef0c0fdcfe7838eff6ff104f9cdec2922297537')]
}),
address: account.address,
privateKey: account.privateKey,
gas: 1
}
```

and ultimately we call doInvoke as a promise

```js
Neon.doInvoke(config).then(res => {
console.log(res)
})
```

In the console result, you will find:
* the data of your config object
* the balance of your account (which is queried from either neoscan or neon-wallet-db)
* the signed transaction
* the response from the JSON RPC
* the url of the NEO node it communicated with

A high-level example of a response:

```js
{
"address": /* the user's address */,
"balance": {
/* an overview of your balance */
},
"gas": 1,
"net": "http://localhost:5000",
"privateKey": /* the user's private key */,
"response": {
"id": 1234,
"jsonrpc": "2.0",
"result": true,
"txid": "763d2d61703f59f014d05052e14c856175b464c59b18a80fa68cd40c71d5d369"
},
"script": /* the script generated from Neon.create.script */,
"tx": {
/* a copy of the transaction doInvoke sent */
},
"url": /* the url of the NEO node the transaction was sent to */
}
```
6 changes: 6 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"eslint-plugin-standard": "^3.0.1",
"mocha": "^5.0.4",
"nyc": "^11.5.0",
"typescript": "^2.7.2",
"webpack": "^4.1.1",
"webpack-cli": "^2.0.11",
"zopfli-webpack-plugin": "^0.1.0"
Expand Down Expand Up @@ -50,6 +51,7 @@
"test": "./node_modules/.bin/mocha ./test",
"test:unit": "./node_modules/.bin/mocha ./test/unit",
"test:integration": "./node_modules/.bin/mocha ./test/integration",
"test:typings": "./node_modules/.bin/tsc",
"cover": "cross-env NODE_ENV=test ./node_modules/.bin/nyc ./node_modules/.bin/mocha ./test && ./node_modules/.bin/nyc report --reporter=lcov",
"cover:unit": "cross-env NODE_ENV=test ./node_modules/.bin/nyc ./node_modules/.bin/mocha ./test/unit && ./node_modules/.bin/nyc report --reporter=lcov",
"cover:integration": "cross-env NODE_ENV=test ./node_modules/.bin/nyc ./node_modules/.bin/mocha ./test/integration && ./node_modules/.bin/nyc report --reporter=lcov",
Expand All @@ -61,7 +63,7 @@
"build:docs": "cd docs && sphinx-versioning -g ../ -l ./conf.py build docs ../docs/_build/html",
"deploy:docs": "cd docs && sphinx-versioning -g ../ -l ./conf.py push docs gh-pages .",
"rebuild": "rm -rf ./node_modules && npm install && ./node_modules/.bin/webpack",
"prepublishOnly": "npm run lint && npm run build:prod"
"prepublishOnly": "npm run test:typings && npm run lint && npm run build:prod"
},
"repository": {
"type": "git",
Expand Down
2 changes: 1 addition & 1 deletion src/api/typings/neonDB.d.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Balance, Claims } from '../../wallet'
import { RPCResponse } from '../../rpc'
import { Fixed8 } from '../../utils'
import { signingFunction, net, AssetAmounts } from './core';
import { signingFunction, net, AssetAmounts, PastTransaction } from './core';

/** API Switch for MainNet and TestNet */
export function getAPIEndpoint(net: net): string
Expand Down
2 changes: 1 addition & 1 deletion src/api/typings/neoscan.d.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Balance, Claims } from '../../wallet'
import { Fixed8 } from '../../utils'
import { net } from './core';
import { net, PastTransaction } from './core';

/** Returns the appropriate NeoScan endpoint. */
export function getAPIEndpoint(net: net): string
Expand Down
6 changes: 4 additions & 2 deletions src/rpc/typings/Network.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { Protocol } from './Protocol'
import { RPCClient } from './client'

export interface NetworkLike {
name?: string
Expand All @@ -18,10 +20,10 @@ export class Network {
constructor(config: NetworkLike, name?: string)

/** Imports a Network from a jsonString */
static import(jsonLike: object, name?: string)
static import(jsonLike: object, name?: string): Network

/** Reads a Network file */
static readFile(filePath: string, name?: string)
static readFile(filePath: string, name?: string): Network

/** Export this class as a object */
export(protocolOnly?: boolean): NetworkLike
Expand Down
8 changes: 5 additions & 3 deletions src/sc/ScriptBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,12 +50,14 @@ class ScriptBuilder extends StringStream {
this.str += hexstring
} else if (size < 0x10000) {
this.emit(OpCode.PUSHDATA2)
this.str += num2hexstring(size, 2)
this.str += num2hexstring(size, 2, true)
this.str += hexstring
} else {
} else if (size < 0x100000000) {
this.emit(OpCode.PUSHDATA4)
this.str += num2hexstring(size, 4)
this.str += num2hexstring(size, 4, true)
this.str += hexstring
} else {
throw new Error(`String too big to emit!`)
}
return this
}
Expand Down
4 changes: 2 additions & 2 deletions src/sc/core.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export const createScript = (...scriptIntents) => {
/**
* Generates script for deploying contract
*/
export const generateDeployScript = ({ script, name, version, author, email, description, needsStorage = false, returnType = 'ff', paramaterList = undefined }) => {
export const generateDeployScript = ({ script, name, version, author, email, description, needsStorage = false, returnType = 'ff', parameterList = undefined }) => {
const sb = new ScriptBuilder()
sb
.emitPush(str2hexstring(description))
Expand All @@ -37,7 +37,7 @@ export const generateDeployScript = ({ script, name, version, author, email, des
.emitPush(str2hexstring(name))
.emitPush(needsStorage)
.emitPush(returnType)
.emitPush(paramaterList)
.emitPush(parameterList)
.emitPush(script)
.emitSysCall('Neo.Contract.Create')
return sb
Expand Down
21 changes: 19 additions & 2 deletions test/unit/sc/core.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@

import { createScript } from '../../../src/sc/core'
import { createScript, generateDeployScript } from '../../../src/sc/core'
import data from './data.json'

describe('SC Core', function () {
describe.only('SC Core', function () {
describe('createScript', function () {
it('single intent', () => {
Object.keys(data).map(key => {
Expand Down Expand Up @@ -47,4 +47,21 @@ describe('SC Core', function () {
result.should.equal(expected)
})
})

describe('generateDeployScript', function () {
it('generate deploy script', () => {
const script = generateDeployScript({
script: '54c56b6c766b00527ac46c766b51527ac46c766b00c36c766b51c3936c766b52527ac46203006c766b52c3616c7566',
name: 'Add',
version: '1',
author: 'Ethan Fast',
email: '[email protected]',
description: 'Add',
returnType: 5,
parameterList: '05'

})
script.str.should.equal('034164640d7465737440746573742e636f6d0a457468616e2046617374013103416464005501052f54c56b6c766b00527ac46c766b51527ac46c766b00c36c766b51c3936c766b52527ac46203006c766b52c3616c756668134e656f2e436f6e74726163742e437265617465')
})
})
})
Loading

0 comments on commit a311721

Please sign in to comment.