@salaku/js-sdk

js-sdk

Usage no npm install needed!

<script type="module">
  import salakuJsSdk from 'https://cdn.skypack.dev/@salaku/js-sdk';
</script>

README

js-sdk

安装

npm install --save @salaku/js-sdk

基本使用

常量

const tdsSDK = require('@salaku/js-sdk')
console.log(tdsSDK.constants)

选择加密算法(全局)

const tdsSDK = require('@salaku/js-sdk')
tdsSDK.setHash('sm3') // keccak-256 sha3-256
tdsSDK.setEC('sm2') // ed25519

事务签名

const tdsSDK = require('@salaku/js-sdk')
const tx = {
    "version" : 1634693120,
    "type" : 0,
    "createdAt" : 1596091121,
    "nonce" : 223814,
    "from" : "",
    "gasPrice" : 0,
    "amount" : 20,
    "payload" : "02b507fe1afd0cc7a525488292beadbe9f143784de44f8bc1c991636509fd50936",
    "to" : "9cbf30db111483e4b84e77ca0e39378fd7605e1b",
}

tdsSDK.sign(tx, 'f00df601a78147ffe0b84de1dffbebed2a6ea965becd5d0bd7faf54f1f29c6b5')
console.log(tx.signature)

计算事务哈希值

const tdsSDK = require('@salaku/js-sdk')
const tx = tdsSDK.Transaction.clone({
    "version" : 1634693120,
    "type" : 0,
    "createdAt" : 1596091121,
    "nonce" : 223814,
    "from" : "",
    "gasPrice" : 0,
    "amount" : 20,
    "payload" : "02b507fe1afd0cc7a525488292beadbe9f143784de44f8bc1c991636509fd50936",
    "to" : "9cbf30db111483e4b84e77ca0e39378fd7605e1b",
})

console.log(tx.getHash())

公钥转地址

const tdsSDK = require('@salaku/js-sdk')
const pk = '02a4d7ca616e70897b7874460da1aab58b56e54c200a08e52a04c69df26b138859' // 公钥
console.log(tdsSDK.publicKey2Address(pk))

生成随机的私钥

const tdsSDK = require('@salaku/js-sdk')
const sk = tdsSDK.generatePrivateKey() // 私钥
console.log(sk)

私钥转公钥

const tdsSDK = require('@salaku/js-sdk')
const sk = 'f00df601a78147ffe0b84de1dffbebed2a6ea965becd5d0bd7faf54f1f29c6b5' // 私钥
console.log(tdsSDK.privateKey2PublicKey(sk))

计算合约地址

const tdsSDK = require('@salaku/js-sdk')
const pk = '02a4d7ca616e70897b7874460da1aab58b56e54c200a08e52a04c69df26b138859' // 公钥
const addr = tdsSDK.publicKey2Address(pk) // 合约构造者的地址
const nonce = '100' // nonce
console.log(tdsSDK.getContractAddress(addr, nonce))

编译 assembly script 合约(异步),需要 node 环境

const tdsSDK = require('@salaku/js-sdk')
const src = 'index.ts'
tdsSDK.compileContract(src)
    .then(buf => console.log('buf length ' + buf.length))

生成 assembly script 合约 abi

const fs = require('fs')
tdsSDK.compileABI(fs.readFileSync('assembly/coin.ts'))

使用事务构造器 TransactionBuilder

const sk = 'f00df601a78147ffe0b84de1dffbebed2a6ea965becd5d0bd7faf54f1f29c6b5' // 私钥
const builder = new tdsSDK.TransactionBuilder(tdsSDK.constants.POA_VERSION, sk, /* gas limit */ 0, /* gas price */ 0)

sk 私钥选填,如果不填私钥,事务的 from 为空,而且事务不会被签名 如果 builder 的 nonce 为0,则构造好的事务不会被签名,如果 builder 的 nonce 不为0,则构造出的事务都是已经签名过的事务,且每构造一个事务 builder 的 nonce 会自增 1

构造转账事务

const tx = builder.buildTransfer(1000, '****address****')

构造 POA 加入请求事务

const tx = builder.buildAuthJoin(constants.PEER_AUTHENTICATION_ADDR)
tx.nonce = 1
builder.sign(tx)

构造 POA 同意请求事务

const tx = builder.buildAuthApprove(constants.PEER_AUTHENTICATION_ADDR, '***同意的地址***')

构造 POA 退出事务

const tx = builder.buildAuthExit(constants.PEER_AUTHENTICATION_ADDR)

构造 POS 投票事务

const tx = builder.buildVote(1000, '***address***')

构造 POS 取消投票事务

const tx = builder.buildCancelVote('***transaction hash***')

RPC 工具类

const rpc = new tdsSDK.RPC('localhost', 7010) // 连接 http, ws
const secureRPC = new tdsSDK.RPC('localhost', 443, true) // 连接 https, wss

查看账户

const pk = '02a4d7ca616e70897b7874460da1aab58b56e54c200a08e52a04c69df26b138859' // 公钥或者地址
rpc.getAccount(pk)
    .then(console.log)

获取 nonce

const pk = '02a4d7ca616e70897b7874460da1aab58b56e54c200a08e52a04c69df26b138859' // 公钥
rpc.getNonce(pk)
    .then(nonce => console.log(`nonce = ${nonce}`))

发送事务(异步)

const tx = tdsSDK.Transaction.clone({
    "version" : 1634693120,
    "type" : 0,
    "createdAt" : 1596091121,
    "nonce" : 223814,
    "from" : "",
    "gasPrice" : 0,
    "amount" : 20,
    "payload" : "02b507fe1afd0cc7a525488292beadbe9f143784de44f8bc1c991636509fd50936",
    "to" : "9cbf30db111483e4b84e77ca0e39378fd7605e1b",
})

rpc.sendTransaction(tx)
.then(() => console.log('success'))
.catch(console.error)

发送事务并等待事务进入区块

const tx = tdsSDK.Transaction.clone({
    "version" : 1634693120,
    "type" : 0,
    "createdAt" : 1596091121,
    "nonce" : 223814,
    "from" : "",
    "gasPrice" : 0,
    "amount" : 20,
    "payload" : "02b507fe1afd0cc7a525488292beadbe9f143784de44f8bc1c991636509fd50936",
    "to" : "9cbf30db111483e4b84e77ca0e39378fd7605e1b",
})

rpc.sendObserve(tx) 
.then(console.log) // 这里会打印出事务所在的区块以及gas消耗、手续费等信息
.catch(console.error)

查看区块(异步)

rpc.getBlock(1) // 可以填区块哈希值或者区块高度
.then(console.log)
.catch(console.error)

查看区块头(异步)

rpc.getHeader(1) // 可以填区块哈希值或者区块高度
.then(console.log)
.catch(console.error)

查看事务(异步)

rpc.getTransaction('****') // 填写事务哈希
.then(console.log)
.catch(console.error)
// 事务的 confrims 字段 -1 表示在内存池中
// 0 或者以上表示被确认, confirms 表示确认数量
// 如出现异常,表示事务不存在

查看 POA 的 P2P 网络申请列表

rpc.getAuthPending(constants.PEER_AUTHENTICATION_ADDR)
.then(console.log)
.catch(console.error)

查看 POA 的 P2P 网络已加入列表

rpc.getAuthNodes(constants.PEER_AUTHENTICATION_ADDR)
.then(console.log)
.catch(console.error)

查看 POS 投票列表

rpc.getPoSNodeInfos()
    .then(console.log)
    .catch(console.error)

POS 根据事务哈希查看投票

rpc.getPoSVoteInfo('**txhash**')
    .then(console.log)
    .catch(console.error)

撤回投票

撤回投票事务的 from 必须是投票事务的 from

const tx = builder.buildCancelVote('c2458cad4c838b81b49500d9268b352b03cd1b78423c113f1b11f41c4ce13657')

智能合约编译部署和调用,假设入口文件如下

/**
 * erc 20 example in assembly script
 */

export { __change_t, __malloc, __peek } from './node_modules/@salaku/js-sdk/lib' // 区块链应用程序接口
import { Util, U256, Globals, ABI_DATA_TYPE } from './node_modules/@salaku/js-sdk/lib'
import { Store } from './node_modules/@salaku/js-sdk/lib'
import { Context, Address } from './node_modules/@salaku/js-sdk/lib'

const _balance = Store.from<Address, U256>('balance');
const _freeze = Store.from<Address, U256>('freeze');

export function init(tokenName: string, symbol: string, totalSupply: U256, decimals: u64, owner: Address): void {
    // tokenName || symbol || totalSupply || decimals || owner
    Globals.set<string>('tokenName', tokenName);
    Globals.set<string>('symbol', symbol);
    Globals.set<U256>('totalSupply', totalSupply);
    Globals.set<u64>('decimals', decimals);
    Globals.set<Address>('owner', owner);
    _balance.set(owner, totalSupply);
}

// display balance
export function balanceOf(addr: Address): U256 {
    return _balance.getOrDefault(addr, U256.ZERO);
}


/* Send coins */
export function transfer(to: Address, amount: U256): void {
    const msg = Context.msg();
    assert(amount > U256.ZERO, 'amount is not positive');
    let b = balanceOf(msg.sender)
    assert(b >= amount, 'balance is not enough');
    _balance.set(to, balanceOf(to) + amount);
    _balance.set(msg.sender, balanceOf(msg.sender) - amount);
}

构造合约部署事务

const contract = new tdsSDK.Contract()
// 编译合约得到字节码
contract.binary = await tdsSDK.compileContract(ascPath, 'assembly/coin.ts')
// 得到 abi
contract.abi = tdsSDK.compileABI(fs.readFileSync('assembly/coin.ts'))
// 写入 abi 文件
fs.writeFilesSync('coin.abi.json', JSON.stringify(contract.abi))
// 获取 nonce 
builder.nonce = (await rpc.getNonce(addr)) + 1

// 传入合约构造器参数
let tx = builder.buildDeploy(contract, {
    tokenName: 'doge',
    symbol: 'DOGE',
    totalSupply: '90000000000000000',
    decimals: 8,
    owner: addr
}, 0)

构造合约调用事务

const abi = require('./coin.abi.json') // abi 文件
const contract = new tdsSDK.Contract('****address****', abi)
const tx = builder.buildContractCall(contract, 'transfer', ['****address****', 100000000], 0)

查看合约

const abi = require('./coin.abi.json') // abi 文件
const contract = new tdsSDK.Contract('****address****', abi)
rpc.viewContract(contract, 'balanceOf', ['****address****'])
    .then(console.log)

生成 keystore

const password = 'abc123'
const ks = tdsSDK.createKeyStore(password)
console.log(ks)

读取 keystore 中私钥

const sk = tdsSDK.readKeyStore(
   {"publicKey":"0230632aa4863e9c0c027de7542b434adae169a8c439f595b244696bce306c8191","crypto":{"cipher":"sm4-128-ecb","cipherText":"f347b48dd1931d66d2e5bb8f4d030b3c5c5db9f32c3c763e6fadcd77de1b6886","iv":"5811eafed7a69b7f84c6b62d473afb0e","salt":"8d5196266292ff95778290983139ec427edea8639d0db37b9298924bf42879e6"},"id":"504e5c56-32da-41f8-8096-5160eda5936c","version":"1","mac":"a57423d830fd5267402838d7dc4e2ec6c54f8120797c369bc412b08585c32c88","kdf":"sm2-kdf","address":"521e3435917a9fe7b3e2841d8f9cf943758bf3e1"},
    "123456"
)

创建poa认证服务并启动

// ['03e3198bd3c12ab68566fcf14903dee3456cd1e1d8a0121ca04aade9b752750216'] 是公钥白名单,只有持有此公钥对应的私钥的节点才能访问这个服务
const server = tdsSDK.createAuthServer(sk, ['03e3198bd3c12ab68566fcf14903dee3456cd1e1d8a0121ca04aade9b752750216'])

server.listen(3000, function () {
    console.log('服务器启动成功了,可以通过 http://127.0.0.1:3000/ 来进行访问')
})