Instant Obyte devnet network set up and testing
- Quick Start
- Usage
- Testkit API
- Network API
- Nodes API
- Utils
- Custom Nodes
- Test Examples
- Writing Tests With Mocha
To quick start with autonomous agent development run the following command:
# requires npm 5.2+ installed
npx create-aa my-agent
This command will prepare the project and the environment from template for AA development.
Also check the VScode extension for AA development and testing
// import test framework
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
// spin up new devnet
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
// create new wallet and send some bytes from rich genesis node
const wallet = await network.newHeadlessWallet().ready()
const walletAddress = await wallet.getAddress()
const { unit } = await genesis.sendBytes({ toAddress: walletAddress, amount: 1000000 })
// and wait for witnessing
await network.witnessUntilStable(unit)
// get wallet balance
const walletBalance = await wallet.getBalance()
Note: Node internal logs with usefull information can be found in
testdata/runid-{runId}/{nodeId}/.config/{nodeType}/log.txt
Defines how to import and configure aa-testkit
module
Testkit constructor function. Helps to configure network defaults
config
- network defaults configuration object
Property | Type | Required | Default | Description |
---|---|---|---|---|
TESTDATA_DIR | String | false | node_modules/aa-testkit/testdata |
Absolute path to testdata directory |
NETWORK_PORT | Number | false | 6611 | Defines port, the network will be run on |
WALLETS_ARE_SINGLE_ADDRESS | Boolean | false | true | Defines if HeadlessWallet nodes are single address by default |
Example
const { Testkit } = require('aa-testkit')
const path = require('path')
const { Network } = Testkit({
// should be absolute path
TESTDATA_DIR: path.join(__dirname, '../testdata'),
NETWORK_PORT: 5000,
})
Alternatively, if you are using node-config you can configure Testkit
as aa-testkit
submodule
Configuration with node-config
// config/default.js
const path = require('path')
module.exports = {
'aa-testkit': {
TESTDATA_DIR: path.join(__dirname, '../testdata'),
NETWORK_PORT: 6611,
},
}
// then you can import `aa-testkit` and `config` variables will be used
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
Primary way to operate with network. Contains common functions for network management
Creates new devnet network from the scratch. .run()
should be called after .create()
for network to start. Starts GenesisNode
and ObyteHub
node, the required minimum for network to operate. GenesisNode
also provides functions of network witness. GenesisNode
has a lot (1e15 - 821
) of Bytes on its account.
genesisParams
- object passed to GenesisNode
constructor to override default values. See GenesisNode Nodes API section.
hubParams
- object passed to ObyteHub
constructor to override default values. See ObyteHub Nodes API section.
Example
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
A set of helpers for running network with some preconditions e.g. wallets running with balances, agents deployed and assets created.
Create a wallet with some bytes or assets balances on its account.
walletName
- a name for the wallet; the wallet node will be available as network.wallet.walletName
balances
- initial balances of this wallet. Could be a number for the Bytes balance or object
mnemonic
- optional field. Allows to specify mnemonic of the wallet
{
base: 1e9,
someasset: 1000, // asset keys are defined in `.with.asset` call
asset2: 1e6
}
with.wallet
can be chained to create multiple wallets
Create an agent. Multiple agents creation is supported
agentName
- a name for the agent; agent address will be avaliable in network.agents.agentName
source
- string with agent or absolute path to the file with agent or anything supported by wallet.deployAgent(source)
Create an asset. Multiple assets creation is supported
assetName
- a name for the asset; asset unit hash will be avaliable avaliable in network.asset.assetName
assetDefinition
- object containing asset definition. Refer to wallet.createAsset(assetDefinition)
for example. Any required field of the definition can be omitted, omitted fields will be replaced with default values
{
is_private: false,
is_transferrable: true,
auto_destroy: false,
issued_by_definer_only: true,
cosigned_by_definer: false,
spender_attested: false,
fixed_denominations: false,
}
Specify number of witnesses defined in the network
number
- number of witnesses. Default is 3
Create custom node with balances
MyNode
- class with implementation of Custom.Node
nodeName
- name for node to be available via network.custom.nodeName
balances
- same as for .with.wallet
Asset or agents deployed with with
helpers are deployed with extra node called deployer
. Deployer node will be avaliable as network.deployer
only if with.agent
or with.asset
was used
Example
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create()
.with.agent({ bouncer: path.join(__dirname, './files/bouncer.oscript') })
.with.asset({ someasset: {} })
.with.wallet({ alice: 1e6 })
.with.wallet({ bob: { base: 1000 } })
.with.wallet({ eva: { base: 1e9, someasset: 1e9 } })
.with.wallet({ mark: { someasset: 1e10 } })
.run()
// wallets are avaliable like this
const balances = await network.wallet.alice.getBalance()
const { unit, error } = await network.wallet.bob.sendBytes({ toAddress: await network.wallet.bob.getAddress(), amount: 100e9 })
const { unitObj } = await network.wallet.eva.getUnitInfo({ unit: unit })
const mark = network.wallet.mark
// agent address
const agentAddress = network.agent.bouncer
// asset unit hash
const asset = network.asset.someasset
// deployer node
const deployer = network.deployer
Also refer to withFeature test code example
Start network with the Obyte Explorer node
port
- port to run Obyte Explorer on. Default 8080
Returns GenesisNode
of this network
Example
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
// calling `.ready()` assures that node have started at this point
const genesis = await network.getGenesisNode().ready()
Returns ObyteHub
of this network
Example
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
// calling `.ready()` assures that node have started at this point
const hub = await network.getHub().ready()
Creates and starts new HeadlessWallet
node in network.
params
- object passed to HeadlessWallet
constructor to override default values. See HeadlessWallet Nodes API section.
Example
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
const wallet = await network.newHeadlessWallet().ready()
Creates and starts new ObyteExplorer
node in network.
params
- object passed to ObyteExplorer
constructor to override default values. See ObyteExplorer Nodes API section.
Example
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
const explorer = await network.newObyteExplorer().ready()
Creates and starts new Custom.Node
node in network.
MyNode
- implementation of Custom.Node
Example
const MyNode = require('./MyNode')
const custom = await network.newCustomNode(MyNode).ready()
Wait for MCI on every node to be synchronized. Returns Promise
that will be resolved when MCI on every node become identical.
Post witnesse until unit
becomes stable. Promise
will be resolved when unit
becomes stable.
unit: String
- wait for stabilization of this unit
Example
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
// create wallet and send bytes to it
const wallet = await network.newHeadlessWallet().ready()
const walletAddress = await wallet.getAddress()
const { unit } = await genesis.sendBytes({ toAddress: walletAddress, amount: 1000000 })
// witness last transaction
await network.witnessUntilStable(unit)
Post witness transactions until unit
becomes stable on node node
.
node: Node
- wait for stabilization on this node
unit: String
- wait for stabilization of this unit
Example
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
const wallet = await network.newHeadlessWallet().ready()
const address = await wallet.getAddress()
const { unit } = await genesis.sendBytes({ toAddress: address, amount: 100000 })
await network.witnessUntilStableOnNode(wallet, unit)
Retrieve autonomous agent execution response from unit
. The method will make the network post witnesses until a response is received. Waits for the response on genesis node. If the unit unit
triggers several AAs at the same time, the behavior of this function is undefined, use getAaResponseToUnitByAA(unit, aa)
instead.
Returns Promise that resolves to { response }
where response
is the object of agent response
unit : String
- unit of aa execution to retrieve response from
Example
Response Object example
{
mci: 13,
trigger_address: 'VL53Z3AQWQ7AX4QKFBA42B3YPY3UYIGK',
trigger_initial_address: 'VL53Z3AQWQ7AX4QKFBA42B3YPY3UYIGK',
trigger_unit: 'N9PI6hN+vmVMkmXMh58pFZErGt638Fwr6yhLQw3g3HA=',
aa_address: 'WWHEN5NDHBI2UF4CLJ7LQ7VAW2QELMD7',
bounced: false,
response_unit: 'm2vasHgREngt/7f0y4g2d42A8Ih+A9iu5dBWGU5pnWg=',
objResponseUnit: {
version: '2.0dev',
alt: '3',
timestamp: 1574171283,
messages: [[Object], [Object]],
authors: [[Object]],
last_ball_unit: 'myDMlLHavXhVF+IMkXS6ir/GkXFASXplaNzDCpsV/kA=',
last_ball: 'jfh9QBcZVXsWE+Q5pstRQO1STO7gfngx9pLIVAxhsUM=',
witness_list_unit: 'rC7dZW1x3OCw8Bh+6urKN5OB0rnsmNeNy6Exz3n+rZI=',
parent_units: ['N9PI6hN+vmVMkmXMh58pFZErGt638Fwr6yhLQw3g3HA='],
headers_commission: 267,
payload_commission: 259,
unit: 'm2vasHgREngt/7f0y4g2d42A8Ih+A9iu5dBWGU5pnWg=',
},
response: {
responseVars:
{ team_asset: 'm2vasHgREngt/7f0y4g2d42A8Ih+A9iu5dBWGU5pnWg=' },
},
updatedStateVars: {
WWHEN5NDHBI2UF4CLJ7LQ7VAW2QELMD7: {
team_VL53Z3AQWQ7AX4QKFBA42B3YPY3UYIGK_founder_tax: [Object],
team_VL53Z3AQWQ7AX4QKFBA42B3YPY3UYIGK_asset: [Object],
},
},
}
Same as network.getAaResponseToUnit
but with the parameter to specify a node
Returns Promise that resolves to { response }
where response
is the object of agent response
node : Node
- node to wait AA response on
unit : String
- unit of aa execution to retrieve response from
Same as network.getAaResponseToUnit
but with a parameter to specify an AA whose response to get. Use it when the triggering transaction triggers several AAs.
Returns Promise that resolves to { response }
where response
is the object of agent response
unit : String
- unit of aa execution to retrieve response from
aa : String
- address of the AA whose response to get
Same as network.getAaResponseToUnit
but with parameters to specify a node to retrieve information from and an AA whose response to get.
Returns Promise that resolves to { response }
where response
is the object of agent response
node : Node
- node to wait AA response on
unit : String
- unit of aa execution to retrieve response from
aa : String
- address of the AA whose response to get
Allows to set the current network time in the future. This can be helpful for testing time-dependent AA.
Note:
timetravel
should be used only after every node has been started. Running a node after timetravel can lead to network inconsistency.
Note: If traveling when time is frozen, the time will be frozen to the new traveled time
Returns Promise that resolves to { error, timestamp }
after timetravel
is done. timestamp
is current timestamp on network after timetravel
. error
will be null if timetravel
call was successfull
to
- new time of the network. Can be either a timestamp or a string in the valid format of Date()
function
shift
- an offset for a current network time. You can pass a Number
for a milliseconds or a String
in format %number%duration
%duration | Example | Result |
---|---|---|
's' | '5s' | shift in 5 seconds |
'm' | '10m' | shift in 10 minutes |
'h' | '24h' | shift in 24 hours |
'd' | '365d' | shift in 365 days |
If both of to
and shift
are present, to
will be used
Example
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
const { error } = await network.timetravel({ to: '2050-01-01' })
Also check in Test Examples
Allows to stop time running on the nodes in the network. This can be helpful for testing time-dependent AA.
Note:
timefreeze
should be used only after every node has been started. Running a node aftertimefreeze
call can lead to network inconsistency.
Note: If traveling when time is frozen, the time will be frozen to the new traveled time
Returns Promise that resolves to { error }
after timefreeze
is done. error
will be null if timefreeze
was successfull
This function resumes time running on the nodes in the network. Time resumes from the last time-traveled position.
Returns Promise that resolves to { error }
after timerun
is done. error
will be null if timerun
was successfull
Send the command to every node to stop the process.
Example
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
const wallet = await network.newHeadlessWallet().ready()
// wait until every node exits
await network.stop()
Section describes all aspects supported by nodes. This aspects includes constructor parameters, methods and events
Note: Most of node constructor parameters are not required when using Network API
Any node can be created directly without using Network API. Although, it is not common but it allows you to create fully custom network
Every node runs inside its own child process. Main process sends commands to nodes and receives messages from them via Node.js IPC.
Creating nodes manually
const { Testkit } = require('aa-testkit')
const path = require('path')
const testdata = path.join(__dirname, '../testdata')
const { Nodes } = Testkit({
TESTDATA_DIR: testdata,
})
const rundir = path.join(testdata, 'custom')
const genesisNode = new Nodes.GenesisNode({
rundir,
id: 'genesis-node',
})
const { genesisUnit, genesisAddress } = await genesisNode.createGenesis()
// Hub will be created and started after genesis node because hub needs to know genesis unit and its main witness(genesis node)
const hub = new Nodes.ObyteHub({
rundir: rundir,
genesisUnit: genesisUnit,
initialWitnesses: [genesisAddress],
id: 'obyte-hub',
})
await genesisNode.ready()
await hub.ready()
// wait for genesis node to login to hub node
await genesisNode.loginToHub()
const wallet = new Nodes.HeadlessWallet({
rundir: rundir,
genesisUnit: genesisUnit,
id: 'headless-wallet-1',
})
await wallet.ready()
const walletAddress = await wallet.getAddress()
// do something usefull here ...
await genesisNode.stop()
await wallet.stop()
await hub.stop()
Methods described in this section are applicable for any node
Returns Promise that resolves when node child will be ready to operate
Returns Promise that resolves when node child exited its process
Returns Promise that resolves to joint
object when node child receives and validates new joint
Joint object example
{
unit:
{
version: '2.0dev',
alt: '3',
messages: [ [Object] ],
authors: [ [Object] ],
timestamp: 1576682858,
parent_units: [ 'A7QwnTCN2kKspj8Mfs5MQGkM9oMoaMvZmIQzq3VWZEM=' ],
last_ball: '0hlnU+5lXnT5s8j22DJQ6OYPdRI3ymqJ1JG0zvcJ9OE=',
last_ball_unit: 'jrEkfzJMZ6mSituUJBN/YOjO56rkopZPT04JGfs6BSo=',
witness_list_unit: 'kTGo2ttgTUkj8bjF6lWKL+afeJm3OIjO+wUJBe0a0fc=',
headers_commission: 402,
payload_commission: 197,
unit: 'ZvGvEjo7/nZ5P70XAGzafqcjqeeww/1mT6zi/fVTfUk='
}
}
Returns Promise that resolves when node receives joint with specified unit
id
Returns Promise that resolves when node receives joint for every unit id in units
array
units
- array of unit ids
Send the command to node child to change its time. This can be helpful for testing time-dependent AA.
Note: Timetravel should be used only after every node has been started. Running a node after timetravel can lead to network inconsistency.
Returns Promise that resolves to { error, timestamp }
when node child receives mci_became_stable
event. timestamp
is current timestamp on network after timetravel
. error
will be null if timetravel was successfull
to
- new time of the network. Can be either a timestamp or a string in the valid format of Date()
function
shift
- an offset for a current network time. You can pass a Number
for a milliseconds or a String
in format %number%duration
%duration | Example | Result |
---|---|---|
's' | '5s' | shift in 5 seconds |
'm' | '10m' | shift in 10 minutes |
'h' | '24h' | shift in 24 hours |
'd' | '365d' | shift in 365 days |
If both of to
and shift
are present, to
will be used
Receive details about unit from node. Uses ocore/storage.readJoint
method
Promise resolves as { time }
object and time
is in milliseconds
Get node last Main Chain Index
Promise resolves as mci
Receive details about unit from node. Uses ocore/storage.readJoint
method
Promise resolves as { unitObj, error }
. Error will be null if everything is ok
unit : String
- unit to get info about
Example
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
const wallet = await network.newHeadlessWallet().ready()
const walletAddress = await wallet.getAddress()
const { unit, error } = await genesis.sendBytes({ toAddress: walletAddress, amount: 1000000 })
await network.witnessUntilStable(unit)
const { unitObj, error } = await wallet.getUnitInfo({ unit: unit })
await network.stop()
UnitObj example
{
"version": "2.0dev",
"alt": "3",
"messages": [
{
"app": "payment",
"payload_location": "inline",
"payload_hash": "5RTdFnLEHBjY21RltWguOazPNePfnU5LtOR+9VqrFa0=",
"payload": {
"outputs": [
{
"address": "EGPUJ2WKQMMOD75BMX5YJNYM2BWA22B5",
"amount": 989401
},
{
"address": "PVMCXUZBEHCFWOLXUDQVNCQZ476LNEW4",
"amount": 10000
}
],
"inputs": [
{
"unit": "9Iw/Lh1a8ESX/1i+38gzcKeKUwu6TJLefEo7v2oQL6o=",
"message_index": 0,
"output_index": 1
}
]
}
}
],
"authors": [
{
"address": "E5O6LEUDV7QMGFCRF2NGAI65UWSZTXR2",
"authentifiers": {
"r": "9c6rYZMJojuiXOErgcxKtj7uoCbQVyX4zfAUJRHGv3Yjlpy30fWThwijedTl6eav7T9f28xfSzPOj9mPW7MzoQ=="
},
"definition": [
"sig",
{
"pubkey": "A6V7juMjcMYwf/Cb7agvWYuMbPK79BgX2r0twuIM2JjF"
}
]
}
],
"timestamp": 1572427125,
"parent_units": [
"7Geraevi9Sj3gcdaZH/Rf9AU2V6Caja5TbH4a5+Fwnc="
],
"last_ball": "oXWxUu7e8PyNqOJoHaDFZIwOjtWCSJGV5L4zbhbWn0E=",
"last_ball_unit": "vHbaBbjajc90AY1/2sDohehFE9Rha0skDnwKnmiWMGc=",
"witness_list_unit": "RlBMDxYFXnYjlPHyS4GGevPiYZJR5a3xNao0/mHQXRY=",
"headers_commission": 402,
"payload_commission": 197,
"unit": "BKf6iaQFZEd9TzsHrg+IDtN1KHjHMYB2zxn8rZZqNcs=",
"main_chain_index": 7
}
Receive unit props.
Promise resolves as { unitProps }
unit : String
- unit to get info about
Example
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
const wallet = await network.newHeadlessWallet().ready()
const walletAddress = await wallet.getAddress()
const { unit, error } = await genesis.sendBytes({ toAddress: walletAddress, amount: 1000000 })
await network.witnessUntilStable(unit)
const { unitProps } = await wallet.getUnitProps({ unit: unit })
await network.stop()
UnitProps example
{
unit: 'WhTBfbZK9k1/SxiINGarg455SdpiQNloBdrzbcO29Lo=',
timestamp: 1574685652,
level: 1,
latest_included_mc_index: 0,
main_chain_index: 1,
is_on_main_chain: 1,
is_free: 0,
is_stable: 1,
witnessed_level: 0,
headers_commission: 355,
payload_commission: 197,
sequence: 'good',
author_addresses: ['ZWDAO5YRB3SQZWAOGVNS4NADGIR3RB7Z'],
witness_list_unit: 'PdiMP8/V2dks18gIP5foxy4WWFYwyURkKyCXQfkZjPU=',
parent_units: ['PdiMP8/V2dks18gIP5foxy4WWFYwyURkKyCXQfkZjPU='],
}
Retrieve current network vars state of agent
Returns Promise that resolves to { vars }
where vars
- state object of agent
address : String
- address of agent to retrieve state from
Agent Deployment example
Get balance of the specified address on the network
Returns Promise that resolves to node balance object
Balance object example
{
base:{
stable:0,
pending:49401,
is_private:null
}
}
Get outputs balance of the specified address on the network. Do not count comissions earned by this node.
Returns Promise that resolves to node balance object
Execute getter of given AA.
Returns Promise that resolves to { result, error }
, where result
a result of getter execution. error
will be null on success
aaAddress : String
- address of the AA
getter : String
- getter identifier
args : Array
- array of parameters to pass to getter for execution
Genesis node main function is to start new network and create genesis unit. After this, genesis node serves as witness and source of Bytes. At the moment of network genesis, this node puts on its account 1e15 - 821
Bytes
Property | Type | Required | Default | Description |
---|---|---|---|---|
id | String | true | Unique id of this node. Also determines node folder in testdata directory |
|
rundir | String | true | Determines where this node will store its data. Absolute path | |
hub | String | false | 'localhost:6611' |
Address of hub to connect |
mnemonic | String | false | Mnemonic to create genesis node from |
Creates network with new genesis unit.
Returns Promise that resolves when genesis complete. Promise resolves to { genesisUnit, genesisAddress }
Example
const { Testkit } = require('aa-testkit')
const path = require('path')
const testdata = path.join(__dirname, '../testdata')
const { Nodes } = Testkit({
TESTDATA_DIR: testdata,
})
const rundir = path.join(testdata, 'custom')
const genesisNode = new Nodes.GenesisNode({
rundir,
id: 'genesis-node',
})
const { genesisUnit, genesisAddress } = await genesisNode.createGenesis()
Send the command to node child to connect to hub. Usefull at network genesis, because hub node starts after genesis node.
Returns Promise that resolves when node connected to hub
Broadcast new witness to network. Returns Promise that resolves to witness unit hash after broadcasting. Although it does not mean other nodes already received it
Returns Promise that resolves to witness unit hash
Example
const { Testkit } = require('aa-testkit')
const path = require('path')
const testdata = path.join(__dirname, '../testdata')
const { Nodes } = Testkit({
TESTDATA_DIR: testdata,
})
const rundir = path.join(testdata, 'custom')
const genesisNode = new Nodes.GenesisNode({
rundir,
id: 'genesis-node',
})
const { genesisUnit, genesisAddress } = await genesisNode.createGenesis()
const hub = new Nodes.ObyteHub({
rundir: rundir,
genesisUnit: genesisUnit,
initialWitnesses: [genesisAddress],
id: 'obyte-hub',
})
await genesisNode.ready()
await hub.ready()
await genesisNode.loginToHub()
const wallet = new Nodes.HeadlessWallet({
rundir: rundir,
genesisUnit: genesisUnit,
id: 'headless-wallet-1',
})
await wallet.ready()
const walletAddress = await wallet.getAddress()
// get something to witness
const { unit, error } = await genesisNode.sendBytes({ toAddress: walletAddress, amount: 1000000 })
const witnessUnit = await genesisNode.postWitness()
await Promise.all([genesisNode.waitForUnit(witnessUnit), wallet.waitForUnit(witnessUnit)])
await genesisNode.stop()
await hub.stop()
await wallet.stop()
Request node address from child.
Returns Promise that resolves to node address
Send Bytes to address
Returns Promise that resolves to { unit, error }
after Bytes are sent. error
will be null if sending was successfull
toAddress : String
- address of node that will receive Bytes
amount : Number
- amount of Bytes to send
Send arbitrary data to the network. Same as wallet.sendMulti
Obyte hub node serves as transport for network. Node receives incomming messages and broadcats them to other nodes
Property | Type | Required | Default | Description |
---|---|---|---|---|
id | String | true | Unique id of this node. Also determines node folder in testdata directory |
|
rundir | String | true | Determines where this node will store its data. Absolute path | |
genesisUnit | String | true | The very first unit of the network | |
port | Number | false | 6611 | Port the hub will be running on |
initialWitnesses | Array[String] | true | Trusted witnesses of this node |
Broadcasts any joint to the network without validating it.
joint : Object
- joint object will be passed directly into network.broadcastJoint()
of ocore
Headless wallet node provides common network node functionality. It can receive Bytes, send Bytes and broadcast messages
Property | Type | Required | Default | Description |
---|---|---|---|---|
id | String | true | Unique id of this node. Also determines node folder in testdata directory |
|
rundir | String | true | Determines where this node will store its data. Absolute path | |
mnemonic | String | false | Mnemonic phrase for wallet | |
genesisUnit | String | true | The very first unit of the network | |
hub | String | false | 'localhost:6611' |
Address of the hub to connect |
isSingleAddress | Boolean | false | true | Defines if node will operate in single address mode |
Request node address from child.
Returns Promise that resolves to node address
Retrieve node balance from child.
Returns Promise that resolves to node balance object
Balance object example
{
base:{
stable:0,
pending:49401,
is_private:null
}
}
Send Bytes to address
Returns Promise that resolves to { unit, error }
after Bytes are sent. error
will be null if sending was successfull
toAddress : String
- address of node that will receive Bytes
amount : Number
- amount of Bytes to send
Trigger AA execution with some data
Returns Promise that resolves to { unit, error }
after data is sent. error
will be null if sending was successfull
toAddress : String
- address of AA to trigger
amount : Number
- amount of Bytes to send to AA
data : Object
- key:value pairs object of data that will be passed to the AA
Allows to broadcast arbitrary message to network. Opts object is passed directly to issueChangeAddressAndSendMultiPayment
method of headless-wallet
Returns Promise that resolves to { unit, error }
after message is sent. error
will be null if sending was successfull
opts : Object
- arbitrary message to broadcast
Issue divisible asset
Returns Promise that resolves to { unit, error }
after message is sent. error
will be null if sending was successfull
opts : Object
- issue params
Example
const opts = {
asset: this.assets[name],
paying_addresses: [myAddress],
fee_paying_addresses: [myAddress],
change_address: myAddress,
to_address: myAddress,
amount: assetSettings.cap,
}
const { unit, error } = await this.deployer.issueDivisibleAsset(opts)
Issue indivisible asset
Returns Promise that resolves to { unit, error }
after message is sent. error
will be null if sending was successfull
opts : Object
- issue params
Example
const opts = {
asset: this.assets[asset],
asset_outputs: outputsMap[asset],
paying_addresses: [myAddress],
fee_paying_addresses: [myAddress],
change_address: myAddress,
tolerance_plus: 0,
tolerance_minus: 0,
}
const { unit, error } = await this.deployer.issueIndivisibleAsset(opts)
Deploy an agent in the network.
Returns Promise that resolves to { address, unit, error }
, where address
is the address of deployed agent and unit
determines unit where agent was deployed. error
will be null on success
source : Path | String | OJSON
- source of AA, can be:
- an absolute path to a file containing plaintext agent
- an absolute path to a javascript file that exports agent string
- an absolute path to a javascript file that exports object in OJSON format
- String with valid AA
- Javascript object in OJSON format
Example
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
const deployer = await network.newHeadlessWallet().ready()
const deployerAddress = await deployer.getAddress()
// send some bytes to AgentDeployer so it will be able to broadcast message with agent to the network
const { unit, error } = await genesis.sendBytes({ toAddress: deployerAddress, amount: 1000000 })
await network.witnessUntilStable(unit)
// agent in OJSON fromat
const agent = {
bounce_fees: { base: 10000 },
messages: [
{
app: 'state',
state: `{
var['constant_var'] = 'constant_var';
var['trigger_var'] = trigger.data.var;
var['sum_var'] = 123 + 456;
}`
}
]
}
// deploy agent and confirm it on the network
const { address, unit, error } = await deployer.deployAgent(agent)
await network.witnessUntilStable(unit)
await network.stop()
Create an asset in the network.
Returns Promise that resolves to { unit, error }
, where unit
determines the asset that was created. error
will be null on success
source : Object
- asset definition
Example
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
const creator = await network.newHeadlessWallet().ready()
const creatorAddress = await deployer.getAddress()
// send some bytes to AgentDeployer so it will be able to broadcast message
const { unit, error } = await genesis.sendBytes({ toAddress: creatorAddress, amount: 1000000 })
await network.witnessUntilStable(unit)
const assetDefinition = {
is_private: false,
is_transferrable: true,
auto_destroy: false,
issued_by_definer_only: true,
cosigned_by_definer: false,
spender_attested: false,
fixed_denominations: false
}
// create asset and confirm it on the network
const { unit, error } = await creator.createAsset(assetDefinition)
await network.witnessUntilStable(unit)
await network.stop()
Retreive the array of addresses, owned by this wallet
Returns Promise that resolves to array of string associated with wallet
after message is sent.
Sign a message with the wallet address
Returns Promise that resolves to { signedPackage, error }
, where signedPackage
is an object containing the signed message and its authentifiers. error
will be null on success
message : String | Object | Array | Number
- something that has to be signed
Composes new unit and returns it, optionally saving it into wallet storage and broadcasting it.
Returns Promise that resolves to { unit, error }
, where unit
is the object of unit composed. error
will be null on success
opts : Object
- options directly passed to composer.composeJoint()
saveJoint : Boolean
- whether to save joint in own storage or not
broadcastJoint : Boolean
- whether to broadcast the joint to the network. You can use .broadcastJoint()
method of Hub to later broadcast it.
Example
const { unit: unitObj } = await aliceWallet.composeJoint({
opts: {
inputs: [
{
message_index: 0,
output_index: 0,
unit: aliceInputUnit1,
},
],
input_amount: 100000,
outputs: [
{ address: aliceAddress, amount: 0 },
],
},
saveJoint: false,
broadcastJoint: false,
})
Signs the unit using it's authors. Technically, this methods returns the same unit but with authentifiers filled for each author of the unit.
Returns Promise that resolves to { unit, error }
, where unit
is the object of signed unit. error
will be null on success
unit : Object
- unit object
Obyte explorer node provides DAG explorer of the network for visualization and debugging purposes
Property | Type | Required | Default | Description |
---|---|---|---|---|
id | String | true | Unique id of this node. Also determines node folder in testdata directory |
|
rundir | String | true | Determines where this node will store its data. Absolute path | |
genesisUnit | String | true | The very first unit of the network | |
webPort | Number | false | 8080 | Port theDAG explorer will be started on |
initialWitnesses | Array[String] | true | Trusted witnesses of this node | |
hub | String | false | 'localhost:6611' |
Address of the hub to connect |
ObyteExplorer node does not have any special methods except common node methods
ObyteExplorer creation example
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
// to have something to display in explorer
const wallet = await network.newHeadlessWallet().ready()
const walletAddress = await wallet.getAddress()
const { unit: unit1 } = await genesis.sendBytes({ toAddress: walletAddress, amount: 1000000 })
await network.witnessUntilStable(unit1)
const { unit: unit2 } = await genesis.sendBytes({ toAddress: walletAddress, amount: 1000000 })
await network.witnessUntilStable(unit2)
// by default DAG explorer will be started on http://localhost:8080
const explorer = await network.newObyteExplorer().ready()
Utils and helpers from aa-testkit
. Can be imported from the package
const { Utils } = require('aa-testkit')
// sleep 5 seconds
await Utils.sleep(5000)
Pause test execution for ms
number of milliseconds. Returns Promise
Helper for address validation. Return true
if passed argument is valid network address, false
otherwise.
Helper for base64 strings validation. Return true
if passed argument is valid network address, false
otherwise.
b64
- string to validate
len
- optional. Length of the string. Function also validates length if second parameter is present
Returns randomly generated mnemonic phrase valid for HeadlessWallets
Returns first wallet address for a given mnemonic pharse.
mnemonic
- valid mnemonic phrase to derive pubkey from
Util that starts n
of HeadlessWallet
nodes asynchronously. Helps to speed up test execution when starting a lot of HeadlessWallet
nodes.
Returns Returns Promise
that resolves to Array of HeadlessWallets
:
network
- network to start nodes on
n
- number of nodes to start
Example
const { Testkit, Utils } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
const [wallet1, wallet2, wallet3] = await Utils.asyncStartHeadlessWallets(network, 3)
await network.stop()
Mocha Example
This could be helpfull if you want to assign created wallets to this
in mocha
tests
this.network = await Network.create().run()
this.genesis = await this.network.getGenesisNode().ready()
this.teamRed = {}
this.teamBlue = {}; // mind the semicolon. Otherwise use of destructuring assignment on the next line will lead to incorrect syntax.
[
this.deployer,
this.teamRed.founder,
this.teamRed.alice,
this.teamRed.bob,
this.teamBlue.founder,
this.teamBlue.mark,
this.teamBlue.eva,
] = await Utils.asyncStartHeadlessWallets(this.network, 7)
Same as asyncStartHeadlessWallets
, but this function starts wallets for known array of mnemonics. Order of wallet nodes in returning array is preserved
Returns Returns Promise
that resolves to Array of HeadlessWallets
:
network
- network to start nodes on
mnemonics
- an array of mnemonic phrases
Helper to count headers and payload commissions in units.
Returns Returns Promise
that resolves to object:
{
error,
total_headers_commission,
total_payload_commission
}
total_headers_commission
is the sum of headers commissions from passed units.
total_payload_commission
is the sum of payload commissions from passed units.
error
will be null
if no errors present.
error
will be the first faced error otherwise.
total_payload_commission
and total_headers_commission
will be null
if error
is present
node
- any running node that supports node.getUnitInfo
units
- array of unit hashes to count the commissions on
Example
const { Testkit, Utils } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
const wallet = await network.newHeadlessWallet().ready()
const { unit, error } = await genesis.sendBytes({
toAddress: await wallet.getAddress(),
amount: 1e9,
})
const commissions = await Utils.countCommissionInUnits(wallet, [unit])
await network.stop()
Helper to check the outputs of a response unit. Returns true
if the unit contains exclusively the payment described in arrExpectedPayments.
unit
- unit object
arrExpectedPayments
- array of payment objects having each this structure
{
address,
amount,
asset
}
where address
is the recipient address, amount
the amount sent, asset
is the asset hash (null
for payment in bytes)
Example
const { Testkit, Utils } = require('aa-testkit')
const { Network, Utils } = Testkit()
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
const sender = await network.newHeadlessWallet().ready()
const { unit } = await genesis.sendBytes({ toAddress: await sender.getAddress(), amount: 1e9 })
await network.witnessUntilStable(unit)
const asset_1 = (await sender.createAsset({
is_private: false,
is_transferrable: true,
auto_destroy: false,
issued_by_definer_only: true,
cosigned_by_definer: false,
spender_attested: false,
fixed_denominations: false,
})).unit
await network.witnessUntilStable(asset_1)
const { unit } = await sender.sendMulti({
asset_outputs: [{
address: 'WDZZ6AGCHI5HTS6LJD3LYLPNBWZ72DZI',
amount: 80006,
}, {
address: '3W43U3SHKBVDUP7T7YOJOY5NM353HA5C',
amount: 3806,
}],
base_outputs: [{
address: '3W43U3SHKBVDUP7T7YOJOY5NM353HA5C',
amount: 1875513,
}],
change_address: await sender.getAddress(),
asset: asset_1,
})
const { unitObj } = await sender.getUnitInfo({ unit })
const isValid = Utils.hasOnlyTheseExternalPayments(unitObj, [{
address: 'WDZZ6AGCHI5HTS6LJD3LYLPNBWZ72DZI',
amount: 80006,
asset: asset_1,
}, {
address: '3W43U3SHKBVDUP7T7YOJOY5NM353HA5C',
amount: 3806,
asset: asset_1,
}, {
address: '3W43U3SHKBVDUP7T7YOJOY5NM353HA5C',
amount: 1875513,
}])
Returns an array of external payments for a given unit object
unit
- unit object
Custom Node API allows to extend existing app based on headless-obyte and integrate it with aa-testkit
. This allows to automate testing of an app and run it on devnet with tools provided by aa-testkit
.
This API consits of two parts which communicate with each other via IPC: Custom.Node
and Custom.Child
. They are two classes that can be imported from testkit and should be extended by user
To obtain the working template with example run the following command:
# requires npm 5.2+ installed
npx create-aa my-agent -t custom-node
Custom.Node
is responsilbe for:
-
providing an user defined api for
aa-testkit
-
sending and receiving messages from
Custom.Child
Custom.Node
already provides any method available in HeadlessWallet node like getBalance
, sendBytes
, getAddress
etc
const path = require('path')
const { Testkit } = require('aa-testkit')
const { Custom } = Testkit({
TESTDATA_DIR: path.join(process.cwd(), 'testdata'),
})
// implementation should be inherited from Custom.Node
class MyNode extends Custom.Node {
// `childPath` should be overloaded and return absolute path to file with Custom.Child implementation
childPath () {
return path.join(__dirname, './MyChild')
}
// messages that are sent from Custom.Child via `this.sendCustomMessage` can be handled in `handleCustomMessage`
handleCustomMessage (payload) {
switch (payload.type) {
case 'pong':
// use `this.sendCustomMessage` to send arbitrary JSON data to `Custom.Node`
this.emit('pong', payload.data)
break
}
}
// any custom method declared in this class will be available in testkit
myFunction () {
// use Promise based syntax to wait and receive data from child
return new Promise(resolve => {
this.once('pong', (data) => resolve(data))
// to send an arbitrary JSON data to Custom.Child use `this.sendCustomCommand` method
this.sendCustomCommand({ type: 'ping', data: 'hello' })
})
}
}
module.exports = MyNode
Custom.Child
is responsilbe for:
-
controlling user app and exposing its functions to testkit indirectly via
Custom.Node
-
sending and receiving messages from
Custom.Node
Custom.Child
is being run in separate process which also runs user app and exposes its functions to Custom.Node
const path = require('path')
const { Testkit } = require('aa-testkit')
const { Custom } = Testkit({
TESTDATA_DIR: path.join(process.cwd(), 'testdata'),
})
// implementation should be inherited from Custom.Child
class MyNodeChild extends Custom.Child {
run () {
// any initialization code goes here
// this method should require and run user app
}
// `sendCustomCommand` from MyNode interface can be handled here
// messages that are sent from Custom.Node via `this.sendCustomCommand` can be handled in `handleCustomCommand`
async handleCustomCommand (payload) {
switch (payload.type) {
case 'ping':
// use `this.sendCustomMessage` to send arbitrary JSON data to `Custom.Node`
this.sendCustomMessage({ type: 'pong', data: payload.data })
break
}
}
}
// this is mandatory to start child correctly on devnet
const child = new MyNodeChild(process.argv)
// start Custom.Child process and user app
child.start()
After implementing node and child API, this node can be run inside testkit
const MyNode = require('./MyNode')
// running custom node using `.with` syntax
this.network = await Network.create()
// first param is the implementation of Custom.Node, second is balances object like in `.with.wallet` syntax
.with.custom(MyNode, { alice: 100e9 })
.run()
// node is ready to execute methods from Custom.Node
const data = await this.network.custom.alice.myFunction()
// or create custom node directly
const custom = await this.network.newCustomNode(MyNode).ready()
const data = await custom.myFunction()
Setup network and send some payments over the network
const assert = require('assert')
const { Testkit } = require('aa-testkit')
const path = require('path')
const testdata = path.join(__dirname, '../testdata')
const { Network } = Testkit({
TESTDATA_DIR: testdata,
})
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
const alice = await network.newHeadlessWallet().ready()
const bob = await network.newHeadlessWallet().ready()
const aliceAddress = await alice.getAddress()
const bobAddress = await bob.getAddress()
const { unit: unit1 } = await genesis.sendBytes({ toAddress: aliceAddress, amount: 100000 })
await network.sync()
let aliceBalance = await alice.getBalance()
assert(aliceBalance.base.pending === 100000)
await network.witnessUntilStable(unit1)
aliceBalance = await alice.getBalance()
assert(aliceBalance.base.stable === 100000)
const { unit: unit2 } = await alice.sendBytes({ toAddress: bobAddress, amount: 50000 })
await network.sync()
aliceBalance = await alice.getBalance()
assert(aliceBalance.base.pending === 49401)
let bobBalance = await bob.getBalance()
assert(bobBalance.base.pending === 50000)
await network.witnessUntilStable(unit2)
aliceBalance = await alice.getBalance()
assert(aliceBalance.base.stable === 49756)
bobBalance = await bob.getBalance()
assert(bobBalance.base.stable === 50000)
await network.stop()
Agent deployment and reading state vars
const assert = require('assert')
const { Testkit } = require('aa-testkit')
const isValidAddress = require('ocore/validation_utils').isValidAddress
const path = require('path')
const agentString = `{
bounce_fees: { base: 10000 },
messages: [
{
app: 'state',
state: "{
var['constant_var'] = 'constant_var';
var['trigger_var'] = trigger.data.var;
var['sum_var'] = 123 + 456;
}"
}
]
}`
const testdata = path.join(__dirname, '../testdata')
const { Network } = Testkit({
TESTDATA_DIR: testdata,
})
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
// agent deployment
const deployer = await network.newHeadlessWallet().ready()
const deployerAddress = await deployer.getAddress()
const wallet = await network.newHeadlessWallet().ready()
const walletAddress = await wallet.getAddress()
await genesis.sendBytes({ toAddress: deployerAddress, amount: 1000000 })
const { unit } = await genesis.sendBytes({ toAddress: walletAddress, amount: 1000000 })
await network.witnessUntilStable(unit)
const { address: agentAddress, unit: agentUnit } = await deployer.deployAgent(agentString)
assert(isValidAddress(agentAddress))
await network.witnessUntilStable(agentUnit)
// reading state vars
const { unit } = await wallet.triggerAaWithData({
toAddress: agentAddress,
amount: 10000,
data: {
var: 'trigger_var',
},
})
await network.witnessUntilStable(unit)
const { vars } = await deployer.readAAStateVars(agentAddress)
assert(vars.constant_var === 'constant_var')
assert(vars.trigger_var === 'trigger_var')
assert(vars.sum_var === '579')
await network.stop()
Time-dependent AA and network timetravel feature
const assert = require('assert')
const { Testkit } = require('aa-testkit')
const isValidAddress = require('ocore/validation_utils').isValidAddress
const path = require('path')
const agentString = `{
bounce_fees: { base: 10000 },
init: '{
$future_ts = 1893456000; // Jan 1, 2030
}',
messages: {
cases: [
{
if: '{timestamp > $future_ts}',
messages: [
{
app: 'state',
state: '{
var['time'] = 'future';
}'
},
]
},
{
if: '{timestamp <= $future_ts}',
messages: [
{
app: 'state',
state: '{
var['time'] = 'past';
}'
},
]
}
]
}
}`
const testdata = path.join(__dirname, '../testdata')
const { Network } = Testkit({
TESTDATA_DIR: testdata,
})
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
// agent deployment
const deployer = await network.newHeadlessWallet().ready()
const deployerAddress = await deployer.getAddress()
const wallet = await network.newHeadlessWallet().ready()
const walletAddress = await wallet.getAddress()
await genesis.sendBytes({ toAddress: deployerAddress, amount: 1000000 })
const { unit } = await genesis.sendBytes({ toAddress: walletAddress, amount: 1000000 })
await network.witnessUntilStable(unit)
const { address: agentAddress, unit: agentUnit } = await deployer.deployAgent(agentString)
assert(isValidAddress(agentAddress))
await network.witnessUntilStable(agentUnit)
const { unit: unitBeforeTravel } = await wallet.sendBytes({
toAddress: agentAddress,
amount: 10000,
})
await network.witnessUntilStable(unitBeforeTravel)
// check state vars before timetravel
let state = await deployer.readAAStateVars(agentAddress)
assert(state.vars.time === 'past')
// Timetravel network
const { error } = await network.timetravel({ to: '2050-01-01' })
const { unit: unitAfterTravel } = await wallet.sendBytes({
toAddress: agentAddress,
amount: 10000,
})
await network.witnessUntilStable(unitAfterTravel)
state = await deployer.readAAStateVars(agentAddress)
assert(state.vars.time === 'future')
await network.stop()
Get unit info
const assert = require('assert')
const { Testkit } = require('aa-testkit')
const path = require('path')
const testdata = path.join(__dirname, '../testdata')
const { Network } = Testkit({
TESTDATA_DIR: testdata,
})
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
const wallet = await network.newHeadlessWallet().ready()
const walletAddress = await wallet.getAddress()
const { unit } = await genesis.sendBytes({ toAddress: walletAddress, amount: 100000 })
await network.witnessUntilStable(unit)
const { unitObj, error } = await wallet.getUnitInfo({ unit })
assert(error === null)
assert(unitObj.hasOwnProperty('unit'))
assert(unitObj.hasOwnProperty('ball'))
assert(unitObj.hasOwnProperty('version'))
assert(unitObj.hasOwnProperty('alt'))
assert(unitObj.hasOwnProperty('messages'))
assert(unitObj.hasOwnProperty('authors'))
assert(unitObj.hasOwnProperty('timestamp'))
assert(unitObj.hasOwnProperty('parent_units'))
assert(unitObj.hasOwnProperty('last_ball'))
assert(unitObj.hasOwnProperty('last_ball_unit'))
assert(unitObj.hasOwnProperty('witness_list_unit'))
assert(unitObj.hasOwnProperty('headers_commission'))
assert(unitObj.hasOwnProperty('payload_commission'))
assert(unitObj.hasOwnProperty('unit'))
assert(unitObj.unit === unit)
assert(unitObj.hasOwnProperty('main_chain_index'))
assert(unitObj.main_chain_index === 1)
await network.stop()
unitObj content
{
"unit": {
"version": "2.0dev",
"alt": "3",
"messages": [
{
"app": "payment",
"payload_location": "inline",
"payload_hash": "rIqo7sri7Vt9yR/P22zd2UNAM6lNx9eJtTsO8Dp2WUI=",
"payload": {
"outputs": [
{
"address": "KMGCRWPAWBR4G66J5ZPYV2YLYCZ7MS7T",
"amount": 100000
},
{
"address": "MF3BUCZD4ACJWEJH7R6FA5XP23WPYK2M",
"amount": 899448
}
],
"inputs": [
{
"unit": "dnAsJUmfXLEoQEBk2bZ0zKLBChSqfCWUalUOhokQ/x8=",
"message_index": 0,
"output_index": 0
}
]
}
}
],
"authors": [
{
"address": "2VEZBTGBZAQBRUPLMN4QTCN5YHJ5ZIS7",
"authentifiers": {
"r": "Z8Ucy7+yVio6jlvzi2C/ig6IF0fHVYW6idV674rHT2Bn3FV+/XkIrHQ0ClHNKT1iMQwilMLycWnNKmN8adqSSw=="
}
}
],
"timestamp": 1572446684,
"parent_units": [
"dnAsJUmfXLEoQEBk2bZ0zKLBChSqfCWUalUOhokQ/x8="
],
"last_ball": "THj5ba6gkebexKppJVp4TyJ3Qhq9CKSOmSQST7Zifz4=",
"last_ball_unit": "dnAsJUmfXLEoQEBk2bZ0zKLBChSqfCWUalUOhokQ/x8=",
"witness_list_unit": "dnAsJUmfXLEoQEBk2bZ0zKLBChSqfCWUalUOhokQ/x8=",
"headers_commission": 355,
"payload_commission": 197,
"unit": "oGv2HJtcC/SILJfHXo44x43PCHds+HAhnouaqyVD/yE=",
"main_chain_index": 1
},
"ball": "mj9Cze6RocUcuQlLT6G5XOG1UWv28tUSQoYU3YNgUhQ="
}
Get AA execution response
const agentString = `{
bounce_fees: { base: 10000 },
messages: [
{
init: '{
$datafeed = trigger.data.dataFeedPayload;
response['dataFeedAaResponse'] = 'aa response!';
}',
app: 'data_feed',
payload: {
dataFeedPayload: '{
if ($datafeed)
return $datafeed;
'no datafeed provided'
}'
}
}
]
}`
const assert = require('assert')
const { Testkit } = require('aa-testkit')
const { Network } = Testkit()
const network = await Network.create().run()
const genesis = await network.getGenesisNode().ready()
const explorer = await network.newObyteExplorer().ready()
const deployer = await network.newHeadlessWallet().ready()
const deployerAddress = await deployer.getAddress()
const wallet = await network.newHeadlessWallet().ready()
const walletAddress = await wallet.getAddress()
const { unit: unit1 } = await genesis.sendBytes({ toAddress: walletAddress, amount: 1e9 })
await network.witnessUntilStable(unit1)
const { unit: unit2 } = await genesis.sendBytes({ toAddress: deployerAddress, amount: 1e9 })
await network.witnessUntilStable(unit2)
const { address: agentAddress, unit: agentDeploymentUnit, error: agentDeploymentError } = await deployer.deployAgent(agentString)
await network.witnessUntilStable(agentDeploymentUnit)
const { unit, error } = await wallet.triggerAaWithData({
toAddress: agentAddress,
amount: 10000,
data: {
dataFeedPayload: 'this will be a datafeed',
},
})
await network.witnessUntilStable(unit)
const { response } = await network.getAaResponseToUnit(unit)
assert(response.response.responseVars.dataFeedAaResponse === 'aa response!')
const aaResponseUnit = response.response_unit
const { unitObj, error: aaResponseUnitError } = await wallet.getUnitInfo({ unit: aaResponseUnit })
const dataFeedMessage = unitObj.messages.find(e => e.app === 'data_feed')
assert(dataFeedMessage.payload.dataFeedPayload === 'this will be a datafeed')
await network.stop()
Although aa-testkit
can be used with any test runner or even without one, we recommend using mocha for writing tests.
Some examples of mocha tests set up can be found inside test
data of aa-testkit
module