@agoric/cosmic-swingset 0.40.0 → 0.40.1-dev-800c2bb.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Makefile +63 -34
- package/README-bridge.md +0 -3
- package/README.md +0 -2
- package/bin/ag-nchainz +1 -1
- package/bootstrap.json +2 -0
- package/calc-rpcport.js +2 -1
- package/check-validator.cjs +2 -0
- package/economy-template.json +67 -5
- package/jsconfig.json +2 -14
- package/package.json +20 -27
- package/rosetta.json +54 -0
- package/src/chain-main.js +422 -213
- package/src/entrypoint.js +3 -5
- package/src/export-kernel-db.js +430 -0
- package/src/export-storage.js +91 -0
- package/src/helpers/bufferedStorage.js +194 -0
- package/src/{is-entrypoint.js → helpers/is-entrypoint.js} +2 -0
- package/src/{json-stable-stringify.js → helpers/json-stable-stringify.js} +1 -1
- package/src/helpers/make-queue.js +134 -0
- package/src/helpers/process-value.js +133 -0
- package/src/import-kernel-db.js +207 -0
- package/src/kernel-stats.js +88 -18
- package/src/launch-chain.js +265 -206
- package/src/params.js +20 -21
- package/src/sim-chain.js +58 -54
- package/src/sim-params.js +4 -3
- package/transfer.ros +161 -0
- package/src/action-types.js +0 -13
- package/src/make-queue.js +0 -86
- package/src/validate-inbound.js +0 -109
package/src/params.js
CHANGED
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
// @ts-check
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
// @jessie-check
|
|
3
|
+
|
|
4
|
+
import { Fail } from '@agoric/assert';
|
|
5
|
+
import { Nat, isNat } from '@endo/nat';
|
|
4
6
|
|
|
5
7
|
export const stringToNat = s => {
|
|
6
|
-
|
|
8
|
+
typeof s === 'string' || Fail`${s} must be a string`;
|
|
7
9
|
const bint = BigInt(s);
|
|
8
10
|
const nat = Nat(bint);
|
|
9
|
-
|
|
10
|
-
`${nat}`,
|
|
11
|
-
s,
|
|
12
|
-
X`${s} must be the canonical representation of ${nat}`,
|
|
13
|
-
);
|
|
11
|
+
`${nat}` === s || Fail`${s} must be the canonical representation of ${nat}`;
|
|
14
12
|
return nat;
|
|
15
13
|
};
|
|
16
14
|
|
|
@@ -18,15 +16,18 @@ export const stringToNat = s => {
|
|
|
18
16
|
export const parseQueueSizes = queueSizeEntries =>
|
|
19
17
|
Object.fromEntries(
|
|
20
18
|
queueSizeEntries.map(({ key, size }) => {
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
typeof key === 'string' || Fail`Key ${key} must be a string`;
|
|
20
|
+
isNat(size) || Fail`Size ${size} is not a positive integer`;
|
|
23
21
|
return [key, size];
|
|
24
22
|
}),
|
|
25
23
|
);
|
|
26
24
|
|
|
27
25
|
/** @param {Record<string, number>} queueSizes */
|
|
28
|
-
export const
|
|
29
|
-
Object.entries(queueSizes).map(([key, size]) =>
|
|
26
|
+
export const encodeQueueSizes = queueSizes =>
|
|
27
|
+
Object.entries(queueSizes).map(([key, size]) => {
|
|
28
|
+
isNat(size) || Fail`Size ${size} is not a positive integer`;
|
|
29
|
+
return { key, size };
|
|
30
|
+
});
|
|
30
31
|
|
|
31
32
|
// Map the SwingSet parameters to a deterministic data structure.
|
|
32
33
|
export const parseParams = params => {
|
|
@@ -36,24 +37,22 @@ export const parseParams = params => {
|
|
|
36
37
|
queue_max: rawQueueMax,
|
|
37
38
|
} = params;
|
|
38
39
|
Array.isArray(rawBeansPerUnit) ||
|
|
39
|
-
|
|
40
|
+
Fail`beansPerUnit must be an array, not ${rawBeansPerUnit}`;
|
|
40
41
|
const beansPerUnit = Object.fromEntries(
|
|
41
42
|
rawBeansPerUnit.map(({ key, beans }) => {
|
|
42
|
-
|
|
43
|
+
typeof key === 'string' || Fail`Key ${key} must be a string`;
|
|
43
44
|
return [key, stringToNat(beans)];
|
|
44
45
|
}),
|
|
45
46
|
);
|
|
46
47
|
Array.isArray(rawFeeUnitPrice) ||
|
|
47
|
-
|
|
48
|
+
Fail`feeUnitPrice ${rawFeeUnitPrice} must be an array`;
|
|
48
49
|
const feeUnitPrice = rawFeeUnitPrice.map(({ denom, amount }) => {
|
|
49
|
-
|
|
50
|
-
|
|
50
|
+
typeof denom === 'string' || Fail`denom ${denom} must be a string`;
|
|
51
|
+
denom || Fail`denom ${denom} must be non-empty`;
|
|
51
52
|
return { denom, amount: stringToNat(amount) };
|
|
52
53
|
});
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
X`queueMax must be an array, not ${rawQueueMax}`,
|
|
56
|
-
);
|
|
54
|
+
Array.isArray(rawQueueMax) ||
|
|
55
|
+
Fail`queueMax must be an array, not ${rawQueueMax}`;
|
|
57
56
|
const queueMax = parseQueueSizes(rawQueueMax);
|
|
58
57
|
|
|
59
58
|
return { beansPerUnit, feeUnitPrice, queueMax };
|
package/src/sim-chain.js
CHANGED
|
@@ -12,14 +12,15 @@ import anylogger from 'anylogger';
|
|
|
12
12
|
import { makeSlogSender } from '@agoric/telemetry';
|
|
13
13
|
|
|
14
14
|
import { resolve as importMetaResolve } from 'import-meta-resolve';
|
|
15
|
-
import {
|
|
16
|
-
import { makeWithQueue } from '@agoric/
|
|
17
|
-
import { makeBatchedDeliver } from '@agoric/
|
|
18
|
-
import stringify from './json-stable-stringify.js';
|
|
15
|
+
import { Fail } from '@agoric/assert';
|
|
16
|
+
import { makeWithQueue } from '@agoric/internal/src/queue.js';
|
|
17
|
+
import { makeBatchedDeliver } from '@agoric/internal/src/batched-deliver.js';
|
|
18
|
+
import stringify from './helpers/json-stable-stringify.js';
|
|
19
19
|
import { launch } from './launch-chain.js';
|
|
20
20
|
import { getTelemetryProviders } from './kernel-stats.js';
|
|
21
21
|
import { DEFAULT_SIM_SWINGSET_PARAMS, QueueInbound } from './sim-params.js';
|
|
22
22
|
import { parseQueueSizes } from './params.js';
|
|
23
|
+
import { makeQueue, makeQueueStorageMock } from './helpers/make-queue.js';
|
|
23
24
|
|
|
24
25
|
const console = anylogger('fake-chain');
|
|
25
26
|
|
|
@@ -38,14 +39,14 @@ async function makeMapStorage(file) {
|
|
|
38
39
|
await fs.promises.writeFile(file, json);
|
|
39
40
|
};
|
|
40
41
|
|
|
41
|
-
|
|
42
|
-
try {
|
|
42
|
+
await (async () => {
|
|
43
43
|
content = await fs.promises.readFile(file);
|
|
44
|
-
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
return JSON.parse(content);
|
|
45
|
+
})().then(
|
|
46
|
+
obj =>
|
|
47
|
+
Object.entries(obj).forEach(([k, v]) => map.set(k, importMailbox(v))),
|
|
48
|
+
() => {},
|
|
49
|
+
);
|
|
49
50
|
|
|
50
51
|
return map;
|
|
51
52
|
}
|
|
@@ -58,7 +59,6 @@ export async function connectToFakeChain(basedir, GCI, delay, inbound) {
|
|
|
58
59
|
const mailboxStorage = await makeMapStorage(mailboxFile);
|
|
59
60
|
|
|
60
61
|
const argv = {
|
|
61
|
-
ROLE: 'sim-chain',
|
|
62
62
|
giveMeAllTheAgoricPowers: true,
|
|
63
63
|
hardcodedClientAddresses: [bootAddress],
|
|
64
64
|
bootMsg: {
|
|
@@ -70,18 +70,17 @@ export async function connectToFakeChain(basedir, GCI, delay, inbound) {
|
|
|
70
70
|
},
|
|
71
71
|
};
|
|
72
72
|
|
|
73
|
-
const
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
).pathname;
|
|
73
|
+
const url = await importMetaResolve(
|
|
74
|
+
process.env.CHAIN_BOOTSTRAP_VAT_CONFIG ||
|
|
75
|
+
argv.bootMsg.params.bootstrap_vat_config,
|
|
76
|
+
import.meta.url,
|
|
77
|
+
);
|
|
78
|
+
const vatconfig = new URL(url).pathname;
|
|
80
79
|
const stateDBdir = path.join(basedir, `fake-chain-${GCI}-state`);
|
|
81
80
|
function replayChainSends() {
|
|
82
|
-
|
|
81
|
+
Fail`Replay not implemented`;
|
|
83
82
|
}
|
|
84
|
-
function clearChainSends() {
|
|
83
|
+
async function clearChainSends() {
|
|
85
84
|
return [];
|
|
86
85
|
}
|
|
87
86
|
|
|
@@ -92,26 +91,19 @@ export async function connectToFakeChain(basedir, GCI, delay, inbound) {
|
|
|
92
91
|
serviceName: TELEMETRY_SERVICE_NAME,
|
|
93
92
|
});
|
|
94
93
|
|
|
95
|
-
const { LMDB_MAP_SIZE } = env;
|
|
96
|
-
const mapSize = (LMDB_MAP_SIZE && parseInt(LMDB_MAP_SIZE, 10)) || undefined;
|
|
97
94
|
const slogSender = await makeSlogSender({
|
|
98
95
|
stateDir: stateDBdir,
|
|
99
96
|
serviceName: TELEMETRY_SERVICE_NAME,
|
|
100
97
|
env,
|
|
101
98
|
});
|
|
102
99
|
|
|
103
|
-
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
consumeAll: () => {
|
|
107
|
-
const iterable = aqContents;
|
|
108
|
-
aqContents = [];
|
|
109
|
-
return iterable;
|
|
110
|
-
},
|
|
111
|
-
};
|
|
100
|
+
const actionQueueStorage = makeQueueStorageMock().storage;
|
|
101
|
+
const highPriorityQueueStorage = makeQueueStorageMock().storage;
|
|
102
|
+
const actionQueue = makeQueue(actionQueueStorage);
|
|
112
103
|
|
|
113
104
|
const s = await launch({
|
|
114
|
-
|
|
105
|
+
actionQueueStorage,
|
|
106
|
+
highPriorityQueueStorage,
|
|
115
107
|
kernelStateDBDir: stateDBdir,
|
|
116
108
|
mailboxStorage,
|
|
117
109
|
clearChainSends,
|
|
@@ -121,13 +113,11 @@ export async function connectToFakeChain(basedir, GCI, delay, inbound) {
|
|
|
121
113
|
debugName: GCI,
|
|
122
114
|
metricsProvider,
|
|
123
115
|
slogSender,
|
|
124
|
-
mapSize,
|
|
125
116
|
});
|
|
126
117
|
|
|
127
|
-
const { blockingSend, savedHeight
|
|
118
|
+
const { blockingSend, savedHeight } = s;
|
|
128
119
|
|
|
129
120
|
let blockHeight = savedHeight;
|
|
130
|
-
let blockTime = savedBlockTime || scaleBlockTime(Date.now());
|
|
131
121
|
const intoChain = [];
|
|
132
122
|
let nextBlockTimeout = 0;
|
|
133
123
|
|
|
@@ -136,7 +126,7 @@ export async function connectToFakeChain(basedir, GCI, delay, inbound) {
|
|
|
136
126
|
const withBlockQueue = makeWithQueue();
|
|
137
127
|
const unhandledSimulateBlock = withBlockQueue(
|
|
138
128
|
async function unqueuedSimulateBlock() {
|
|
139
|
-
blockTime = scaleBlockTime(Date.now());
|
|
129
|
+
const blockTime = scaleBlockTime(Date.now());
|
|
140
130
|
blockHeight += 1;
|
|
141
131
|
|
|
142
132
|
const params = DEFAULT_SIM_SWINGSET_PARAMS;
|
|
@@ -146,22 +136,27 @@ export async function connectToFakeChain(basedir, GCI, delay, inbound) {
|
|
|
146
136
|
blockTime,
|
|
147
137
|
params,
|
|
148
138
|
};
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
const
|
|
152
|
-
|
|
139
|
+
await blockingSend(beginAction);
|
|
140
|
+
const inboundQueueMax = parseQueueSizes(params.queue_max)[QueueInbound];
|
|
141
|
+
const inboundQueueAllowed = Math.max(
|
|
142
|
+
0,
|
|
143
|
+
inboundQueueMax - actionQueue.size(),
|
|
144
|
+
);
|
|
153
145
|
|
|
154
146
|
// Gather up the new messages into the latest block.
|
|
155
|
-
const thisBlock = intoChain.splice(0,
|
|
156
|
-
|
|
157
|
-
for (const [newMessages, acknum] of thisBlock) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
147
|
+
const thisBlock = intoChain.splice(0, inboundQueueAllowed);
|
|
148
|
+
|
|
149
|
+
for (const [i, [newMessages, acknum]] of thisBlock.entries()) {
|
|
150
|
+
actionQueue.push({
|
|
151
|
+
action: {
|
|
152
|
+
type: 'DELIVER_INBOUND',
|
|
153
|
+
peer: bootAddress,
|
|
154
|
+
messages: newMessages,
|
|
155
|
+
ack: acknum,
|
|
156
|
+
blockHeight,
|
|
157
|
+
blockTime,
|
|
158
|
+
},
|
|
159
|
+
context: { blockHeight, txHash: 'simTx', msgIdx: i },
|
|
165
160
|
});
|
|
166
161
|
}
|
|
167
162
|
const endAction = { type: 'END_BLOCK', blockHeight, blockTime };
|
|
@@ -194,10 +189,12 @@ export async function connectToFakeChain(basedir, GCI, delay, inbound) {
|
|
|
194
189
|
|
|
195
190
|
intoChain.push([newMessages, acknum]);
|
|
196
191
|
// Only actually simulate a block if we're not in bootstrap.
|
|
192
|
+
let p;
|
|
197
193
|
if (blockHeight && !delay) {
|
|
198
194
|
clearTimeout(nextBlockTimeout);
|
|
199
|
-
|
|
195
|
+
p = simulateBlock();
|
|
200
196
|
}
|
|
197
|
+
await p;
|
|
201
198
|
}
|
|
202
199
|
|
|
203
200
|
const bootSimChain = async () => {
|
|
@@ -207,7 +204,10 @@ export async function connectToFakeChain(basedir, GCI, delay, inbound) {
|
|
|
207
204
|
// The before-first-block is special... do it now.
|
|
208
205
|
// This emulates what x/swingset does to run a BOOTSTRAP_BLOCK
|
|
209
206
|
// before continuing with the real initialHeight.
|
|
210
|
-
await blockingSend({
|
|
207
|
+
await blockingSend({
|
|
208
|
+
type: 'BOOTSTRAP_BLOCK',
|
|
209
|
+
blockTime: scaleBlockTime(Date.now()),
|
|
210
|
+
});
|
|
211
211
|
blockHeight = initialHeight;
|
|
212
212
|
};
|
|
213
213
|
|
|
@@ -222,5 +222,9 @@ export async function connectToFakeChain(basedir, GCI, delay, inbound) {
|
|
|
222
222
|
});
|
|
223
223
|
|
|
224
224
|
const batchDelayMs = delay ? delay * 1000 : undefined;
|
|
225
|
-
return makeBatchedDeliver(
|
|
225
|
+
return makeBatchedDeliver(
|
|
226
|
+
deliver,
|
|
227
|
+
{ clearTimeout, setTimeout },
|
|
228
|
+
batchDelayMs,
|
|
229
|
+
);
|
|
226
230
|
}
|
package/src/sim-params.js
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
// @jessie-check
|
|
2
|
+
|
|
3
|
+
import { Nat } from '@endo/nat';
|
|
2
4
|
|
|
3
5
|
const makeStringBeans = (key, beans) => ({ key, beans: `${Nat(beans)}` });
|
|
4
6
|
const makePowerFlagFee = (powerFlag, fee) => ({ powerFlag, fee });
|
|
@@ -58,8 +60,7 @@ export const defaultBeansPerUnit = [
|
|
|
58
60
|
makeStringBeans(BeansPerXsnapComputron, defaultBeansPerXsnapComputron),
|
|
59
61
|
];
|
|
60
62
|
|
|
61
|
-
|
|
62
|
-
'@agoric/vats/decentral-demo-config.json';
|
|
63
|
+
const defaultBootstrapVatConfig = '@agoric/vats/decentral-demo-config.json';
|
|
63
64
|
|
|
64
65
|
export const defaultPowerFlagFees = [
|
|
65
66
|
makePowerFlagFee('SMART_WALLET', [makeCoin('ubld', 10_000_000n)]),
|
package/transfer.ros
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
request_funds(1){
|
|
2
|
+
find_account{
|
|
3
|
+
currency = {"symbol":"ubld", "decimals":0};
|
|
4
|
+
random_account = find_balance({
|
|
5
|
+
"minimum_balance":{
|
|
6
|
+
"value": "0",
|
|
7
|
+
"currency": {{currency}}
|
|
8
|
+
},
|
|
9
|
+
"create_limit":1
|
|
10
|
+
});
|
|
11
|
+
},
|
|
12
|
+
send_funds{
|
|
13
|
+
account_identifier = {{random_account.account_identifier}};
|
|
14
|
+
address = {{account_identifier.address}};
|
|
15
|
+
idk = http_request({
|
|
16
|
+
"method": "POST",
|
|
17
|
+
"url": "http:\/\/127.0.0.1:7000",
|
|
18
|
+
"timeout": 10,
|
|
19
|
+
"body": {{random_account.account_identifier.address}}
|
|
20
|
+
});
|
|
21
|
+
},
|
|
22
|
+
// Create a separate scenario to request funds so that
|
|
23
|
+
// the address we are using to request funds does not
|
|
24
|
+
// get rolled back if funds do not yet exist.
|
|
25
|
+
request{
|
|
26
|
+
loaded_account = find_balance({
|
|
27
|
+
"account_identifier": {{random_account.account_identifier}},
|
|
28
|
+
"minimum_balance":{
|
|
29
|
+
"value": "50",
|
|
30
|
+
"currency": {{currency}}
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
create_account(1){
|
|
36
|
+
create{
|
|
37
|
+
network = {"network":"agoriclocal", "blockchain":"app"};
|
|
38
|
+
key = generate_key({"curve_type": "secp256k1"});
|
|
39
|
+
account = derive({
|
|
40
|
+
"network_identifier": {{network}},
|
|
41
|
+
"public_key": {{key.public_key}}
|
|
42
|
+
});
|
|
43
|
+
// If the account is not saved, the key will be lost!
|
|
44
|
+
save_account({
|
|
45
|
+
"account_identifier": {{account.account_identifier}},
|
|
46
|
+
"keypair": {{key}}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
transferBLD(3){
|
|
51
|
+
transfer{
|
|
52
|
+
transfer.network = {"network":"agoriclocal", "blockchain":"app"};
|
|
53
|
+
currency = {"symbol":"ubld", "decimals":0};
|
|
54
|
+
sender = find_balance({
|
|
55
|
+
"minimum_balance":{
|
|
56
|
+
"value": "1000000",
|
|
57
|
+
"currency": {{currency}}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
acc_identifier = {{sender.account_identifier}};
|
|
61
|
+
sender_address = {{acc_identifier.address}};
|
|
62
|
+
// Set the recipient_amount as some value <= sender.balance-max_fee
|
|
63
|
+
max_fee = "0";
|
|
64
|
+
fee_amount = "1";
|
|
65
|
+
fee_value = 0 - {{fee_amount}};
|
|
66
|
+
available_amount = {{sender.balance.value}} - {{max_fee}};
|
|
67
|
+
recipient_amount = random_number({"minimum": "1", "maximum": {{available_amount}}});
|
|
68
|
+
print_message({"recipient_amount":{{recipient_amount}}});
|
|
69
|
+
// Find recipient and construct operations
|
|
70
|
+
sender_amount = 0 - {{recipient_amount}};
|
|
71
|
+
recipient = find_balance({
|
|
72
|
+
"not_account_identifier":[{{sender.account_identifier}}],
|
|
73
|
+
"minimum_balance":{
|
|
74
|
+
"value": "0",
|
|
75
|
+
"currency": {{currency}}
|
|
76
|
+
},
|
|
77
|
+
"create_limit": 100,
|
|
78
|
+
"create_probability": 50
|
|
79
|
+
});
|
|
80
|
+
transfer.confirmation_depth = "1";
|
|
81
|
+
recipient_account_identifier = {{recipient.account_identifier}};
|
|
82
|
+
recipient_address = {{recipient_account_identifier.address}};
|
|
83
|
+
transfer.operations = [
|
|
84
|
+
{
|
|
85
|
+
"operation_identifier":{"index":0},
|
|
86
|
+
"type":"/cosmos.bank.v1beta1.MsgSend",
|
|
87
|
+
"account":{{sender.account_identifier}},
|
|
88
|
+
"metadata": {
|
|
89
|
+
"amount": [
|
|
90
|
+
{
|
|
91
|
+
"amount": {{recipient_amount}},
|
|
92
|
+
"denom": {{currency.symbol}}
|
|
93
|
+
}
|
|
94
|
+
],
|
|
95
|
+
"from_address": {{sender_address}},
|
|
96
|
+
"to_address": {{recipient_address}}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
];
|
|
100
|
+
transfer.preprocess_metadata = {
|
|
101
|
+
"gas_price": "0.01ubld",
|
|
102
|
+
"gas_limit": 250000
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
transferIST(4){
|
|
107
|
+
transfer{
|
|
108
|
+
transfer.network = {"network":"agoriclocal", "blockchain":"app"};
|
|
109
|
+
currency = {"symbol":"uist", "decimals":0};
|
|
110
|
+
sender = find_balance({
|
|
111
|
+
"minimum_balance":{
|
|
112
|
+
"value": "1000000",
|
|
113
|
+
"currency": {{currency}}
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
acc_identifier = {{sender.account_identifier}};
|
|
117
|
+
sender_address = {{acc_identifier.address}};
|
|
118
|
+
// Set the recipient_amount as some value <= sender.balance-max_fee
|
|
119
|
+
max_fee = "0";
|
|
120
|
+
fee_amount = "1";
|
|
121
|
+
fee_value = 0 - {{fee_amount}};
|
|
122
|
+
available_amount = {{sender.balance.value}} - {{max_fee}};
|
|
123
|
+
recipient_amount = random_number({"minimum": "1", "maximum": {{available_amount}}});
|
|
124
|
+
print_message({"recipient_amount":{{recipient_amount}}});
|
|
125
|
+
// Find recipient and construct operations
|
|
126
|
+
sender_amount = 0 - {{recipient_amount}};
|
|
127
|
+
recipient = find_balance({
|
|
128
|
+
"not_account_identifier":[{{sender.account_identifier}}],
|
|
129
|
+
"minimum_balance":{
|
|
130
|
+
"value": "0",
|
|
131
|
+
"currency": {{currency}}
|
|
132
|
+
},
|
|
133
|
+
"create_limit": 100,
|
|
134
|
+
"create_probability": 50
|
|
135
|
+
});
|
|
136
|
+
transfer.confirmation_depth = "1";
|
|
137
|
+
recipient_account_identifier = {{recipient.account_identifier}};
|
|
138
|
+
recipient_address = {{recipient_account_identifier.address}};
|
|
139
|
+
transfer.operations = [
|
|
140
|
+
{
|
|
141
|
+
"operation_identifier":{"index":0},
|
|
142
|
+
"type":"/cosmos.bank.v1beta1.MsgSend",
|
|
143
|
+
"account":{{sender.account_identifier}},
|
|
144
|
+
"metadata": {
|
|
145
|
+
"amount": [
|
|
146
|
+
{
|
|
147
|
+
"amount": {{recipient_amount}},
|
|
148
|
+
"denom": {{currency.symbol}}
|
|
149
|
+
}
|
|
150
|
+
],
|
|
151
|
+
"from_address": {{sender_address}},
|
|
152
|
+
"to_address": {{recipient_address}}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
];
|
|
156
|
+
transfer.preprocess_metadata = {
|
|
157
|
+
"gas_price": "0.01ubld",
|
|
158
|
+
"gas_limit": 250000
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
package/src/action-types.js
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export const BOOTSTRAP_BLOCK = 'BOOTSTRAP_BLOCK';
|
|
2
|
-
export const BEGIN_BLOCK = 'BEGIN_BLOCK';
|
|
3
|
-
export const CALCULATE_FEES_IN_BEANS = 'CALCULATE_FEES_IN_BEANS';
|
|
4
|
-
export const CORE_EVAL = 'CORE_EVAL';
|
|
5
|
-
export const DELIVER_INBOUND = 'DELIVER_INBOUND';
|
|
6
|
-
export const END_BLOCK = 'END_BLOCK';
|
|
7
|
-
export const COMMIT_BLOCK = 'COMMIT_BLOCK';
|
|
8
|
-
export const IBC_EVENT = 'IBC_EVENT';
|
|
9
|
-
export const PLEASE_PROVISION = 'PLEASE_PROVISION';
|
|
10
|
-
export const VBANK_BALANCE_UPDATE = 'VBANK_BALANCE_UPDATE';
|
|
11
|
-
export const WALLET_ACTION = 'WALLET_ACTION';
|
|
12
|
-
export const WALLET_SPEND_ACTION = 'WALLET_SPEND_ACTION';
|
|
13
|
-
export const INSTALL_BUNDLE = 'INSTALL_BUNDLE';
|
package/src/make-queue.js
DELETED
|
@@ -1,86 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @typedef {object} QueueStorage
|
|
3
|
-
* @property {() => void} commit
|
|
4
|
-
* @property {() => void} abort
|
|
5
|
-
* @property {(key: string) => unknown} get
|
|
6
|
-
* @property {(key: string, value: unknown) => void} set
|
|
7
|
-
* @property {(key: string) => void} delete
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
/**
|
|
11
|
-
* Create a queue backed by some sort of scoped storage.
|
|
12
|
-
*
|
|
13
|
-
* The queue writes the following bare keys, and expect any prefixing/scoping
|
|
14
|
-
* to be handled by the storage:
|
|
15
|
-
* - `head`: the index of the first entry of the queue.
|
|
16
|
-
* - `tail`: the index *past* the last entry in the queue.
|
|
17
|
-
* - `<index>`: the contents of the queue at the given index.
|
|
18
|
-
*
|
|
19
|
-
* For the `actionQueue`, the Cosmos side of the queue will push into the queue,
|
|
20
|
-
* updating `<prefix>tail` and `<prefix><index>`. The JS side will shift the
|
|
21
|
-
* queue, updating `<prefix>head` and reading and deleting `<prefix><index>`.
|
|
22
|
-
*
|
|
23
|
-
* Parallel access is not supported, only a single outstanding operation at a
|
|
24
|
-
* time.
|
|
25
|
-
*
|
|
26
|
-
* @param {QueueStorage} storage a scoped queue storage
|
|
27
|
-
*/
|
|
28
|
-
export const makeQueue = storage => {
|
|
29
|
-
const queue = {
|
|
30
|
-
size: () => {
|
|
31
|
-
const tail = storage.get('tail') || 0;
|
|
32
|
-
const head = storage.get('head') || 0;
|
|
33
|
-
return tail - head;
|
|
34
|
-
},
|
|
35
|
-
push: obj => {
|
|
36
|
-
const tail = storage.get('tail') || 0;
|
|
37
|
-
storage.set('tail', tail + 1);
|
|
38
|
-
storage.set(`${tail}`, obj);
|
|
39
|
-
storage.commit();
|
|
40
|
-
},
|
|
41
|
-
/** @type {Iterable<unknown>} */
|
|
42
|
-
consumeAll: () => ({
|
|
43
|
-
[Symbol.iterator]: () => {
|
|
44
|
-
let done = false;
|
|
45
|
-
let head = storage.get('head') || 0;
|
|
46
|
-
const tail = storage.get('tail') || 0;
|
|
47
|
-
return {
|
|
48
|
-
next: () => {
|
|
49
|
-
if (done) return { done };
|
|
50
|
-
if (head < tail) {
|
|
51
|
-
// Still within the queue.
|
|
52
|
-
const headKey = `${head}`;
|
|
53
|
-
const value = storage.get(headKey);
|
|
54
|
-
storage.delete(headKey);
|
|
55
|
-
head += 1;
|
|
56
|
-
return { value, done };
|
|
57
|
-
}
|
|
58
|
-
// Reached the end, so clean up our indices.
|
|
59
|
-
storage.delete('head');
|
|
60
|
-
storage.delete('tail');
|
|
61
|
-
storage.commit();
|
|
62
|
-
done = true;
|
|
63
|
-
return { done };
|
|
64
|
-
},
|
|
65
|
-
return: () => {
|
|
66
|
-
if (done) return { done };
|
|
67
|
-
// We're done consuming, so save our state.
|
|
68
|
-
storage.set('head', head);
|
|
69
|
-
storage.commit();
|
|
70
|
-
done = true;
|
|
71
|
-
return { done };
|
|
72
|
-
},
|
|
73
|
-
throw: err => {
|
|
74
|
-
if (done) return { done };
|
|
75
|
-
// Don't change our state.
|
|
76
|
-
storage.abort();
|
|
77
|
-
done = true;
|
|
78
|
-
throw err;
|
|
79
|
-
},
|
|
80
|
-
};
|
|
81
|
-
},
|
|
82
|
-
}),
|
|
83
|
-
};
|
|
84
|
-
return queue;
|
|
85
|
-
};
|
|
86
|
-
harden(makeQueue);
|
package/src/validate-inbound.js
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
import { assert, details as X } from '@agoric/assert';
|
|
2
|
-
|
|
3
|
-
import * as ActionType from './action-types.js';
|
|
4
|
-
|
|
5
|
-
const MAX_BODY_DEPTH = 50n;
|
|
6
|
-
|
|
7
|
-
const SMART_WALLET_BODY_KEYS = new Set([
|
|
8
|
-
'',
|
|
9
|
-
'brand',
|
|
10
|
-
'digits',
|
|
11
|
-
'give',
|
|
12
|
-
'id',
|
|
13
|
-
'iface',
|
|
14
|
-
'In',
|
|
15
|
-
'index',
|
|
16
|
-
'instance',
|
|
17
|
-
'invitationSpec',
|
|
18
|
-
'method',
|
|
19
|
-
'offer',
|
|
20
|
-
'Out',
|
|
21
|
-
'proposal',
|
|
22
|
-
'publicInvitationMaker',
|
|
23
|
-
'@qclass',
|
|
24
|
-
'source',
|
|
25
|
-
'value',
|
|
26
|
-
'want',
|
|
27
|
-
]);
|
|
28
|
-
|
|
29
|
-
// From economicCommitteeAddresses in `decentral-main-psm-config.json`
|
|
30
|
-
const SMART_WALLET_EC_COMMITTEE = new Set(
|
|
31
|
-
Object.values({
|
|
32
|
-
'Jason Potts': 'agoric1gx9uu7y6c90rqruhesae2t7c2vlw4uyyxlqxrx',
|
|
33
|
-
'Chloe White': 'agoric1d4228cvelf8tj65f4h7n2td90sscavln2283h5',
|
|
34
|
-
'Thibault Schrepel': 'agoric14543m33dr28x7qhwc558hzlj9szwhzwzpcmw6a',
|
|
35
|
-
'Chris Berg': 'agoric13p9adwk0na5npfq64g22l6xucvqdmu3xqe70wq',
|
|
36
|
-
'Youssef Amrani': 'agoric1el6zqs8ggctj5vwyukyk4fh50wcpdpwgugd5l5',
|
|
37
|
-
'Joe Clark': 'agoric1zayxg4e9vd0es9c9jlpt36qtth255txjp6a8yc',
|
|
38
|
-
}),
|
|
39
|
-
);
|
|
40
|
-
|
|
41
|
-
export const validateWalletAction = async action => {
|
|
42
|
-
if (SMART_WALLET_EC_COMMITTEE.has(action.owner)) {
|
|
43
|
-
// The Economic Committee uses the smart wallet for governance votes
|
|
44
|
-
// which have a less structured payload.
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const actualAction =
|
|
49
|
-
action.type === ActionType.WALLET_SPEND_ACTION
|
|
50
|
-
? action.spendAction
|
|
51
|
-
: action.action;
|
|
52
|
-
assert(
|
|
53
|
-
actualAction !== undefined,
|
|
54
|
-
X`Wallet action undefined for ${action.type}`,
|
|
55
|
-
);
|
|
56
|
-
const actualPayloadBody = JSON.parse(actualAction).body;
|
|
57
|
-
|
|
58
|
-
assert.typeof(actualPayloadBody, 'string');
|
|
59
|
-
assert(
|
|
60
|
-
!actualPayloadBody.startsWith('#'),
|
|
61
|
-
X`Unexpected smallcaps in smart wallet payload`,
|
|
62
|
-
);
|
|
63
|
-
|
|
64
|
-
let bodyMaxDepth = 0n;
|
|
65
|
-
try {
|
|
66
|
-
bodyMaxDepth = JSON.parse(
|
|
67
|
-
actualPayloadBody,
|
|
68
|
-
function reviverCheck(key, value) {
|
|
69
|
-
if (!Array.isArray(this)) {
|
|
70
|
-
assert(
|
|
71
|
-
SMART_WALLET_BODY_KEYS.has(key),
|
|
72
|
-
X`Unexpected key in smart wallet payload: ${key}`,
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
switch (typeof value) {
|
|
77
|
-
case 'object': {
|
|
78
|
-
if (!value) return 0n;
|
|
79
|
-
let maxChildDepth = 0n;
|
|
80
|
-
|
|
81
|
-
for (const childDepth of Object.values(value)) {
|
|
82
|
-
if (childDepth > maxChildDepth) {
|
|
83
|
-
maxChildDepth = childDepth;
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
|
-
return 1n + maxChildDepth;
|
|
87
|
-
}
|
|
88
|
-
case 'boolean':
|
|
89
|
-
case 'number':
|
|
90
|
-
case 'string':
|
|
91
|
-
return 0n;
|
|
92
|
-
default:
|
|
93
|
-
assert.fail(X`Unexpected parsed type`);
|
|
94
|
-
}
|
|
95
|
-
},
|
|
96
|
-
);
|
|
97
|
-
} catch (err) {
|
|
98
|
-
if (err.name === 'SyntaxError') {
|
|
99
|
-
console.warn('Invalid smart wallet body', action, err);
|
|
100
|
-
} else {
|
|
101
|
-
throw err;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
assert(
|
|
106
|
-
bodyMaxDepth < MAX_BODY_DEPTH,
|
|
107
|
-
X`Smart wallet message body too deep: ${bodyMaxDepth}`,
|
|
108
|
-
);
|
|
109
|
-
};
|