Comparing version 0.1.0 to 0.1.1
120
aa_state.js
@@ -23,2 +23,4 @@ "use strict"; | ||
let expectedResponses = {}; | ||
function customizer(value) { | ||
@@ -133,17 +135,110 @@ if (value instanceof wrappedObject) | ||
function getResponseEssentials(objAAResponse) { | ||
const { mci, timestamp, bounced, aa_address, objResponseUnit, response: { responseVars }, balances: b } = objAAResponse; | ||
let balances = _.cloneDeep(b); | ||
delete balances.base; // ignore, fees are approximate | ||
if (objResponseUnit) { | ||
var messages = _.cloneDeep(objResponseUnit.messages); | ||
for (let m of messages) { | ||
delete m.payload_location; | ||
delete m.payload_hash; | ||
if (m.app === 'payment') { | ||
if (!m.payload.asset) | ||
m.payload.asset = 'base'; | ||
delete m.payload.inputs; | ||
m.payload.outputs = m.payload.outputs.filter(o => o.address !== aa_address); | ||
} | ||
} | ||
messages = messages.filter(m => m.app !== 'payment' || m.payload.outputs.length > 0); | ||
messages.sort((m1, m2) => { | ||
if (m1.app < m2.app) | ||
return -1; | ||
if (m1.app > m2.app) | ||
return 1; | ||
if (m1.app === 'payment') | ||
return (m1.payload.asset < m2.payload.asset) ? -1 : 1; | ||
console.log(`unsorted app`, m1, m2); | ||
return 1; | ||
}); | ||
} | ||
return { timestamp, bounced, responseVars, messages, balances }; // mci is always wrong | ||
} | ||
function getMaxDifference(v1, v2) { | ||
if (v1 === v2) | ||
return 0; | ||
if (typeof v1 !== typeof v2) | ||
return Infinity; | ||
switch (typeof v1) { | ||
case 'number': | ||
return Math.abs(v1 - v2) / (v1 + v2) * 2; | ||
case 'string': | ||
if (v1.endsWith('%') && v2.endsWith('%')) { | ||
v1 = v1.slice(0, -1); | ||
v2 = v2.slice(0, -1); | ||
} | ||
try { | ||
// JSON.parse() also converts strings to numbers "123" => 123) | ||
var j1 = JSON.parse(v1); | ||
var j2 = JSON.parse(v2); | ||
} | ||
catch (e) { | ||
return Infinity; | ||
} | ||
return getMaxDifference(j1, j2); | ||
case 'object': | ||
if (Array.isArray(v1) !== Array.isArray(v2)) | ||
return Infinity; | ||
let max_diff = 0; | ||
if (Array.isArray(v1)) { | ||
if (v1.length !== v2.length) | ||
return Infinity; | ||
for (let i = 0; i < v1.length; i++) { | ||
const diff = getMaxDifference(v1[i], v2[i]); | ||
if (diff > max_diff) | ||
max_diff = diff; | ||
} | ||
} | ||
else { | ||
if (!_.isEqual(Object.keys(v1).sort(), Object.keys(v2).sort())) | ||
return Infinity; | ||
for (let key in v1) { | ||
const diff = getMaxDifference(v1[key], v2[key]); | ||
if (diff > max_diff) | ||
max_diff = diff; | ||
} | ||
} | ||
return max_diff; | ||
default: | ||
return Infinity; | ||
} | ||
} | ||
async function onAAResponse(objAAResponse) { | ||
const unlock = await lock(); | ||
console.log(`onAAResponse`, objAAResponse); | ||
const aa_address = objAAResponse.aa_address; | ||
if (objAAResponse.trigger_initial_unit !== last_trigger_unit) { | ||
removeExecutedPendingTriggers(objAAResponse.trigger_initial_unit); | ||
last_trigger_unit = objAAResponse.trigger_initial_unit; | ||
const { aa_address, trigger_address, trigger_unit, trigger_initial_unit, updatedStateVars } = objAAResponse; | ||
const expectedResponse = expectedResponses[trigger_unit]; | ||
if (expectedResponse) { | ||
const essentials = getResponseEssentials(objAAResponse); | ||
const same = _.isEqual(expectedResponse, essentials); | ||
const matches = same ? 'matches' : 'mismatches'; | ||
const relDiff = (field) => (getMaxDifference(expectedResponse[field], essentials[field]) * 100).toPrecision(1) + '%'; | ||
const difference = same ? '' : `, ts ${relDiff('timestamp')}, r ${relDiff('responseVars')}, m ${relDiff('messages')}, b ${relDiff('balances')}`; | ||
console.log(`trigger ${trigger_unit} from ${trigger_address} to ${aa_address}: response ${matches} expectations${difference}`); | ||
if (!same) | ||
console.log('expected', JSON.stringify(expectedResponse, null, 2), 'actual', JSON.stringify(essentials, null, 2)); | ||
delete expectedResponses[trigger_unit]; | ||
} | ||
if (trigger_initial_unit !== last_trigger_unit) { | ||
removeExecutedPendingTriggers(trigger_initial_unit); | ||
last_trigger_unit = trigger_initial_unit; | ||
} | ||
else // we are called several times when a chain is executed | ||
console.log(`repeated response to ${last_trigger_unit}`); | ||
if (objAAResponse.updatedStateVars) { | ||
for (let address in objAAResponse.updatedStateVars) { | ||
if (updatedStateVars) { | ||
for (let address in updatedStateVars) { | ||
if (!stateVars[address]) | ||
stateVars[address] = {}; | ||
let vars = objAAResponse.updatedStateVars[address]; | ||
let vars = updatedStateVars[address]; | ||
for (let var_name in vars) { | ||
@@ -176,9 +271,9 @@ let varInfo = vars[var_name]; | ||
async function onAARequest(objAARequest) { | ||
const aa_address = objAARequest.aa_address; | ||
const objUnit = objAARequest.unit; | ||
if (!objUnit.messages) // final-bad | ||
const { aa_address, unit: objUnit } = objAARequest; | ||
const { unit, messages } = objUnit; | ||
if (!messages) // final-bad | ||
return console.log("no messages"); | ||
const unlock = await lock(); | ||
if (arrPendingTriggers.find(pt => pt.unit.unit === objUnit.unit)) { | ||
console.log(`trigger ${objUnit.unit} already queued`); | ||
if (arrPendingTriggers.find(pt => pt.unit.unit === unit)) { | ||
console.log(`trigger ${unit} already queued`); | ||
return unlock(); | ||
@@ -194,2 +289,3 @@ } | ||
console.log(`--- estimated responses`, JSON.stringify(arrResponses, null, 2)); | ||
expectedResponses[unit] = getResponseEssentials(arrResponses[0]) | ||
arrPendingTriggers.push(objAARequest); | ||
@@ -196,0 +292,0 @@ unlock(); |
@@ -11,7 +11,19 @@ "use strict"; | ||
function getValueBeforeTimestamp(oracle, timestamp) { | ||
let timestamps = Object.keys(oracle.values); | ||
timestamps.sort().reverse(); // from latest | ||
for (let ts of timestamps) | ||
if (ts <= timestamp) { | ||
console.log(`using ${oracle.address}:${oracle.feed_name} df value ${oracle.values[ts]} received at ${new Date(ts * 1000).toISOString()}`); | ||
return oracle.values[ts]; | ||
} | ||
console.log(`oracle ${oracle.address}:${oracle.feed_name}: no past value before ${timestamp}, will use the last known value ${oracle.value}`); | ||
return oracle.value; | ||
} | ||
// replace the function | ||
data_feeds.readDataFeedValue = function (arrAddresses, feed_name, value, min_mci, max_mci, bAA, ifseveral, handleResult) { | ||
data_feeds.readDataFeedValue = function (arrAddresses, feed_name, value, min_mci, max_mci, bAA, ifseveral, timestamp, handleResult) { | ||
for (let oracle of oracles) { | ||
if (arrAddresses.includes(oracle.address) && feed_name === oracle.feed_name) | ||
return handleResult({ value: oracle.value }); | ||
return handleResult({ value: getValueBeforeTimestamp(oracle, timestamp) }); | ||
} | ||
@@ -25,3 +37,3 @@ throw Error("light data feed not found: " + arrAddresses + ":" + feed_name); | ||
return oracle; | ||
let oracle = { address, feed_name, ts: 0 }; | ||
let oracle = { address, feed_name, ts: 0, values: {} }; // values holds past values keyed by timestamp, they are necessary for replaying old triggers | ||
oracles.push(oracle); | ||
@@ -63,4 +75,9 @@ return oracle; | ||
if (oracle.address === address && df[oracle.feed_name]) { | ||
console.log(`received new value of data feed ${oracle.address}:${oracle.feed_name}`, df[oracle.feed_name]); | ||
oracle.value = string_utils.getFeedValue(df[oracle.feed_name]); | ||
console.log(`${objUnit.unit}: received new value of data feed ${oracle.address}:${oracle.feed_name}`, df[oracle.feed_name]); | ||
const value = string_utils.getFeedValue(df[oracle.feed_name]); | ||
oracle.value = value; | ||
oracle.values[objUnit.timestamp] = value; | ||
for (let ts in oracle.values) // delete old values | ||
if (ts < Date.now() / 1000 - 12 * 3600) | ||
delete oracle.values[ts]; | ||
oracle.ts = Date.now(); | ||
@@ -67,0 +84,0 @@ } |
{ | ||
"name": "aabot", | ||
"version": "0.1.0", | ||
"version": "0.1.1", | ||
"engines": { | ||
@@ -5,0 +5,0 @@ "node": ">=8" |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
38801
846