-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
683 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
// @ts-check | ||
// @jessie-check | ||
|
||
const { freeze } = Object; | ||
|
||
const agdBinary = 'agd'; | ||
|
||
/** @param {{ execFileSync: typeof import('child_process').execFileSync }} io */ | ||
export const makeAgd = ({ execFileSync }) => { | ||
console.warn('XXX is sync IO essential?'); | ||
|
||
/** @param {{ home?: string, keyringBackend?: string, rpcAddrs?: string[] }} keyringOpts */ | ||
const make = ({ home, keyringBackend, rpcAddrs } = {}) => { | ||
const keyringArgs = [ | ||
...(home ? ['--home', home] : []), | ||
...(keyringBackend ? [`--keyring-backend`, keyringBackend] : []), | ||
]; | ||
console.warn('XXX: rpcAddrs after [0] are ignored'); | ||
const nodeArgs = [...(rpcAddrs ? [`--node`, rpcAddrs[0]] : [])]; | ||
|
||
// TODO: verbose option | ||
const l = a => { | ||
console.log(a); // XXX unilateral logging by a library... iffy | ||
return a; | ||
}; | ||
|
||
/** | ||
* @param {string[]} args | ||
* @param {*} [opts] | ||
*/ | ||
const exec = (args, opts) => execFileSync(agdBinary, args, opts).toString(); | ||
|
||
const outJson = ['--output', 'json']; | ||
|
||
const ro = freeze({ | ||
status: async () => JSON.parse(exec([...nodeArgs, 'status'])), | ||
/** | ||
* @param { | ||
* | [kind: 'tx', txhash: string] | ||
* | [mod: 'vstorage', kind: 'data' | 'children', path: string] | ||
* } qArgs | ||
*/ | ||
query: async qArgs => { | ||
const out = await exec(['query', ...qArgs, ...nodeArgs, ...outJson], { | ||
stdio: ['ignore', 'pipe', 'ignore'], | ||
}); | ||
try { | ||
return JSON.parse(out); | ||
} catch (e) { | ||
console.error(e); | ||
console.info('output:', out); | ||
} | ||
}, | ||
}); | ||
const nameHub = freeze({ | ||
/** | ||
* @param {string[]} path | ||
* NOTE: synchronous I/O | ||
*/ | ||
lookup: (...path) => { | ||
if (!Array.isArray(path)) { | ||
// TODO: use COND || Fail`` | ||
throw TypeError(); | ||
} | ||
if (path.length !== 1) { | ||
throw Error(`path length limited to 1: ${path.length}`); | ||
} | ||
const [name] = path; | ||
const txt = exec(['keys', 'show', `--address`, name, ...keyringArgs]); | ||
return txt.trim(); | ||
}, | ||
}); | ||
const rw = freeze({ | ||
/** | ||
* TODO: gas | ||
* | ||
* @param {string[]} txArgs | ||
* @param {{ chainId: string, from: string, yes?: boolean }} opts | ||
*/ | ||
tx: async (txArgs, { chainId, from, yes }) => { | ||
const yesArg = yes ? ['--yes'] : []; | ||
const args = [ | ||
...nodeArgs, | ||
...[`--chain-id`, chainId], | ||
...keyringArgs, | ||
...[`--from`, from], | ||
'tx', | ||
...txArgs, | ||
...['--broadcast-mode', 'block'], | ||
...yesArg, | ||
...outJson, | ||
]; | ||
const out = exec(args); | ||
try { | ||
return JSON.parse(out); | ||
} catch (e) { | ||
console.error(e); | ||
console.info('output:', out); | ||
} | ||
}, | ||
...ro, | ||
...nameHub, | ||
readOnly: () => ro, | ||
nameHub: () => nameHub, | ||
keys: { | ||
add: (name, mnemonic) => { | ||
return execFileSync( | ||
agdBinary, | ||
[...keyringArgs, 'keys', 'add', name, '--recover'], | ||
{ input: mnemonic }, | ||
).toString(); | ||
}, | ||
}, | ||
withOpts: opts => make({ home, keyringBackend, rpcAddrs, ...opts }), | ||
}); | ||
return rw; | ||
}; | ||
return make(); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
export const Fail = (template, ...args) => { | ||
throw Error(String.raw(template, ...args.map(val => String(val)))); | ||
}; | ||
|
||
export const assert = (cond, msg = 'check failed') => { | ||
if (!cond) { | ||
throw Error(msg); | ||
} | ||
}; | ||
|
||
assert.typeof = (val, type) => { | ||
if (typeof val !== type) { | ||
throw Error(`expected ${type}, got ${typeof val}`); | ||
} | ||
}; | ||
|
||
/** @type {<T>(val: T | undefined) => T} */ | ||
export const NonNullish = val => { | ||
if (!val) throw Error('required'); | ||
return val; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
File renamed without changes.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
// @ts-check | ||
'use strict'; | ||
|
||
const { | ||
create, | ||
entries, | ||
fromEntries, | ||
freeze, | ||
keys, | ||
setPrototypeOf, | ||
prototype: objectPrototype, | ||
} = Object; | ||
const { isArray } = Array; | ||
|
||
const sigilDoc = { | ||
'!': 'escaped string', | ||
'+': `non-negative bigint`, | ||
'-': `negative bigint`, | ||
'#': `manifest constant`, | ||
'%': `symbol`, | ||
$: `remotable`, | ||
'&': `promise`, | ||
}; | ||
const sigils = keys(sigilDoc).join(''); | ||
|
||
/** @type {<V, U>(obj: Record<string, V>, f: (v: V) => U) => Record<string, U>} */ | ||
const objMap = (obj, f) => | ||
fromEntries(entries(obj).map(([p, v]) => [f(p), f(v)])); | ||
|
||
const { freeze: harden } = Object; // XXX | ||
|
||
const makeMarshal = (_v2s, convertSlotToVal = (s, _i) => s) => { | ||
const fromCapData = ({ body, slots }) => { | ||
const recur = v => { | ||
switch (typeof v) { | ||
case 'boolean': | ||
case 'number': | ||
return v; | ||
case 'string': | ||
if (v === '') return v; | ||
const sigil = v.slice(0, 1); | ||
if (!sigils.includes(sigil)) return v; | ||
switch (sigil) { | ||
case '!': | ||
return v.slice(1); | ||
case '+': | ||
return BigInt(v.slice(1)); | ||
case '-': | ||
return -BigInt(v.slice(1)); | ||
case '$': { | ||
const [ix, iface] = v.slice(1).split('.'); | ||
return convertSlotToVal(slots[Number(ix)], iface); | ||
} | ||
case '#': | ||
switch (v) { | ||
case '#undefined': | ||
return undefined; | ||
case '#Infinity': | ||
return Infinity; | ||
case '#NaN': | ||
return Infinity; | ||
default: | ||
throw RangeError(`Unexpected constant ${v}`); | ||
} | ||
case '%': | ||
// TODO: @@asyncIterator | ||
return Symbol.for(v.slice(1)); | ||
default: | ||
throw RangeError(`Unexpected sigil ${sigil}`); | ||
} | ||
case 'object': | ||
if (v === null) return v; | ||
if (isArray(v)) { | ||
return freeze(v.map(recur)); | ||
} | ||
return freeze(objMap(v, recur)); | ||
default: | ||
throw RangeError(`Unexpected value type ${typeof v}`); | ||
} | ||
}; | ||
const encoding = JSON.parse(body.replace(/^#/, '')); | ||
return recur(encoding); | ||
}; | ||
|
||
const toCapData = () => { | ||
throw Error('not implemented'); | ||
}; | ||
|
||
return harden({ | ||
fromCapData, | ||
unserialize: fromCapData, | ||
toCapData, | ||
serialize: toCapData, | ||
}); | ||
}; | ||
|
||
const PASS_STYLE = Symbol.for('passStyle'); | ||
export const Far = (iface, methods) => { | ||
const proto = freeze( | ||
create(objectPrototype, { | ||
[PASS_STYLE]: { value: 'remotable' }, | ||
[Symbol.toStringTag]: { value: iface }, | ||
}), | ||
); | ||
setPrototypeOf(methods, proto); | ||
freeze(methods); | ||
return methods; | ||
}; | ||
|
||
// #region marshal-table | ||
const makeSlot1 = (val, serial) => { | ||
const prefix = Promise.resolve(val) === val ? 'promise' : 'object'; | ||
return `${prefix}${serial}`; | ||
}; | ||
|
||
const makeTranslationTable = (makeSlot, makeVal) => { | ||
const valToSlot = new Map(); | ||
const slotToVal = new Map(); | ||
|
||
const convertValToSlot = val => { | ||
if (valToSlot.has(val)) return valToSlot.get(val); | ||
const slot = makeSlot(val, valToSlot.size); | ||
valToSlot.set(val, slot); | ||
slotToVal.set(slot, val); | ||
return slot; | ||
}; | ||
|
||
const convertSlotToVal = (slot, iface) => { | ||
if (slotToVal.has(slot)) return slotToVal.get(slot); | ||
if (makeVal) { | ||
const val = makeVal(slot, iface); | ||
valToSlot.set(val, slot); | ||
slotToVal.set(slot, val); | ||
return val; | ||
} | ||
throw Error(`no such ${iface}: ${slot}`); | ||
}; | ||
|
||
return harden({ convertValToSlot, convertSlotToVal }); | ||
}; | ||
// #endregion marshal-table | ||
|
||
export { makeMarshal, makeTranslationTable }; |
Oops, something went wrong.