@ocap/statedb-fs 1.20.15 → 1.21.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/lib/db.js CHANGED
@@ -4,6 +4,7 @@ const Table = require('./table/base');
4
4
  const Account = require('./table/account');
5
5
  const Token = require('./table/token');
6
6
  const Rollup = require('./table/rollup');
7
+ const Balance = require('./table/balance');
7
8
 
8
9
  const { name, version } = require('../package.json');
9
10
 
@@ -14,17 +15,36 @@ class FsStateDB extends StateDB {
14
15
  this.name = name;
15
16
  this.version = version;
16
17
 
17
- this.account = new Account('account', dataDir, 'address');
18
- this.asset = new Table('asset', dataDir, 'address');
19
- this.factory = new Table('factory', dataDir, 'address');
20
- this.delegation = new Table('delegation', dataDir, 'address');
21
- this.tx = new Table('tx', dataDir, 'hash');
22
- this.token = new Token('token', dataDir, 'address');
23
- this.chain = new Table('chain', dataDir, 'address');
24
- this.stake = new Table('stake', dataDir, 'address');
25
- this.rollup = new Rollup('rollup', dataDir, 'address');
26
- this.rollupBlock = new Table('rollupBlock', dataDir, 'hash');
27
- this.evidence = new Table('evidence', dataDir, 'hash');
18
+ this.balance = new Balance({ name: 'balance', dataDir, uniqIndex: ['address', 'tokenAddress'] });
19
+ this.account = new Account({
20
+ name: 'account',
21
+ dataDir,
22
+ uniqIndex: 'address',
23
+ balanceTable: this.balance,
24
+ syncBalance: true,
25
+ });
26
+ this.factory = new Table({
27
+ name: 'factory',
28
+ dataDir,
29
+ uniqIndex: 'address',
30
+ syncBalance: true,
31
+ balanceTable: this.balance,
32
+ });
33
+ this.stake = new Table({
34
+ name: 'stake',
35
+ dataDir,
36
+ uniqIndex: 'address',
37
+ syncBalance: true,
38
+ balanceTable: this.balance,
39
+ });
40
+ this.asset = new Table({ name: 'asset', dataDir, uniqIndex: 'address' });
41
+ this.delegation = new Table({ name: 'delegation', dataDir, uniqIndex: 'address' });
42
+ this.tx = new Table({ name: 'tx', dataDir, uniqIndex: 'hash' });
43
+ this.token = new Token({ name: 'token', dataDir, uniqIndex: 'address' });
44
+ this.chain = new Table({ name: 'chain', dataDir, uniqIndex: 'address' });
45
+ this.rollup = new Rollup({ name: 'rollup', dataDir, uniqIndex: 'address' });
46
+ this.rollupBlock = new Table({ name: 'rollupBlock', dataDir, uniqIndex: 'hash' });
47
+ this.evidence = new Table({ name: 'evidence', dataDir, uniqIndex: 'hash' });
28
48
 
29
49
  this.attachReadyListeners();
30
50
  }
@@ -1,29 +1,19 @@
1
1
  const { ensureChecksumAddress } = require('@ocap/state/lib/states/account');
2
- const debug = require('debug')(require('../../package.json').name);
3
2
  const FsTable = require('./base');
4
3
 
5
4
  class AccountTable extends FsTable {
6
5
  async _get(address, { traceMigration = true } = {}) {
7
- const current = await this.collection.by(this.uniqIndex, ensureChecksumAddress(address));
6
+ const current = await super._get(ensureChecksumAddress(address));
8
7
  if (current && traceMigration && Array.isArray(current.migratedTo) && current.migratedTo.length) {
9
- return this.collection.by(this.uniqIndex, current.migratedTo[0]);
8
+ return this._get(current.migratedTo[0]);
10
9
  }
11
10
 
12
11
  return current;
13
12
  }
14
13
 
15
- async _create(key, attrs) {
16
- const doc = await this._get(key);
17
- if (doc) {
18
- throw new Error(`${this.name} already exists: ${key}`);
19
- }
20
-
21
- debug(`insert ${this.name}`, attrs);
22
- const newDoc = { [this.uniqIndex]: key, ...attrs };
23
- newDoc[this.uniqIndex] = ensureChecksumAddress(newDoc[this.uniqIndex]);
24
- const result = await this.collection.insert(newDoc);
25
-
26
- return result;
14
+ _create(key, attrs = {}, ctx = {}) {
15
+ const address = ensureChecksumAddress(key);
16
+ return super._create(address, { ...attrs, address }, ctx);
27
17
  }
28
18
  }
29
19
 
@@ -0,0 +1,41 @@
1
+ const FsTable = require('./base');
2
+
3
+ class BalanceTable extends FsTable {
4
+ async getBalance(address) {
5
+ const tokens = await this.collection.find({ address });
6
+
7
+ return (tokens || []).reduce((acc, token) => {
8
+ acc[token.tokenAddress] = token.balance;
9
+ return acc;
10
+ }, {});
11
+ }
12
+
13
+ async updateBalance({ address, tokens, context = {} }, ctx) {
14
+ if (!Object.keys(tokens).length) return {};
15
+
16
+ const tokenBalances = await this.getBalance(address);
17
+ const updatedTokens = Object.keys(tokens)
18
+ .filter(
19
+ (token) => (tokenBalances[token] && tokenBalances[token] !== '0') || (tokens[token] && tokens[token] !== '0')
20
+ )
21
+ .filter((token) => tokenBalances[token] !== tokens[token]);
22
+
23
+ await Promise.all(
24
+ updatedTokens.map(async (token) => {
25
+ const key = { address, tokenAddress: token };
26
+
27
+ if (tokenBalances[token]) {
28
+ await this.update(key, { ...key, balance: tokens[token], context }, ctx);
29
+ } else {
30
+ await this.create(key, { ...key, balance: tokens[token], context }, ctx);
31
+ }
32
+
33
+ tokenBalances[token] = tokens[token];
34
+ })
35
+ );
36
+
37
+ return tokens;
38
+ }
39
+ }
40
+
41
+ module.exports = BalanceTable;
package/lib/table/base.js CHANGED
@@ -8,13 +8,27 @@ const FsAdapter = require('lokijs/src/loki-fs-structured-adapter');
8
8
  const debug = require('debug')(require('../../package.json').name);
9
9
 
10
10
  class FsTable extends StateDBTable {
11
- constructor(name, dataDir, uniqIndex) {
12
- super();
11
+ /**
12
+ * @param {Object} param
13
+ * @param {string} param.name db name
14
+ * @param {string|string[]} param.uniqIndex primary keys
15
+ * @param {string} param.dataDir data directory for db
16
+ * @param {boolean} param.syncBalance sync balance table when create / update / get
17
+ * @param {BalanceTable} param.balanceTable balance table
18
+ */
19
+ constructor({ name, dataDir, uniqIndex, balanceTable, syncBalance = false }) {
20
+ super(uniqIndex);
13
21
 
14
22
  this.name = name;
15
23
  this.dataDir = dataDir;
16
24
  this.uniqIndex = uniqIndex;
17
25
  this.collection = null;
26
+ this.balanceTable = balanceTable;
27
+ this.syncBalance = syncBalance;
28
+
29
+ if (this.syncBalance && !this.balanceTable) {
30
+ throw new Error('balanceTable is required when syncBalance is true');
31
+ }
18
32
 
19
33
  const adapter = new FsAdapter();
20
34
  const db = new Lokijs(path.join(dataDir, `${name}.db`), {
@@ -26,7 +40,7 @@ class FsTable extends StateDBTable {
26
40
  this.collection = db.getCollection(name);
27
41
 
28
42
  if (this.collection === null) {
29
- this.collection = db.addCollection(name, { unique: [uniqIndex], clone: true });
43
+ this.collection = db.addCollection(name, { unique: [this.primaryKey], clone: true });
30
44
  }
31
45
 
32
46
  this.markReady();
@@ -35,18 +49,48 @@ class FsTable extends StateDBTable {
35
49
  }
36
50
 
37
51
  async _create(key, attrs) {
38
- const doc = await this._get(key);
52
+ const id = this.generatePrimaryKey(key);
53
+ // Don't call this._get here cause _get may be overwritten
54
+ const doc = await FsTable.prototype._get.call(this, id);
39
55
  if (doc) {
40
56
  throw new Error(`${this.name} already exists: ${key}`);
41
57
  }
58
+
42
59
  debug(`insert ${this.name}`, attrs);
43
- const result = await this.collection.insert({ [this.uniqIndex]: key, ...attrs });
60
+
61
+ const insertAttrs = { ...attrs };
62
+ // insert without tokens cause we've migrate token to balance table
63
+ if (this.syncBalance) {
64
+ delete insertAttrs.tokens;
65
+ }
66
+
67
+ const result = await this.collection.insert({ [this.primaryKey]: id, ...insertAttrs });
68
+
69
+ if (this.syncBalance && attrs.tokens) {
70
+ debug(`update balance for ${this.name}`, attrs);
71
+
72
+ result.tokens = await this.balanceTable.updateBalance({
73
+ address: attrs.address,
74
+ tokens: attrs.tokens,
75
+ context: attrs.context,
76
+ });
77
+ }
44
78
 
45
79
  return result;
46
80
  }
47
81
 
48
- _get(key) {
49
- return key ? this.collection.by(this.uniqIndex, key) : null;
82
+ async _get(key) {
83
+ if (!key) return null;
84
+
85
+ const id = this.generatePrimaryKey(key);
86
+ const result = await this.collection.by(this.primaryKey, id);
87
+
88
+ if (result && this.syncBalance) {
89
+ const balance = await this.balanceTable.getBalance(result.address);
90
+ result.tokens = { ...(result.tokens || {}), ...balance };
91
+ }
92
+
93
+ return result;
50
94
  }
51
95
 
52
96
  _history() {
@@ -54,28 +98,49 @@ class FsTable extends StateDBTable {
54
98
  }
55
99
 
56
100
  async _update(key, updates) {
57
- const doc = await this.collection.by(this.uniqIndex, key);
101
+ const id = this.generatePrimaryKey(key);
102
+ // Don't call this._get here cause _get may be overwritten
103
+ const doc = await FsTable.prototype._get.call(this, id);
58
104
  if (!doc) {
59
105
  throw new Error(`${this.name} does not exists: ${key}`);
60
106
  }
61
107
 
62
- debug(`update ${this.name}`, { key, updates });
63
108
  Object.assign(doc, updates);
64
- await this.collection.update(doc);
109
+
110
+ const updateAttrs = { ...doc };
111
+ // insert without tokens cause we've migrate token to balance table
112
+ if (this.syncBalance) {
113
+ delete updateAttrs.tokens;
114
+ }
115
+
116
+ await this.collection.update(updateAttrs);
117
+
118
+ if (this.syncBalance && doc.tokens) {
119
+ debug(`update balance for ${this.name}`, doc);
120
+
121
+ doc.tokens = await this.balanceTable.updateBalance({
122
+ address: doc.address,
123
+ tokens: doc.tokens,
124
+ context: doc.context,
125
+ });
126
+ }
65
127
 
66
128
  return doc;
67
129
  }
68
130
 
69
131
  updateOrCreate(exist, state, ctx) {
70
- if (!state[this.uniqIndex]) {
132
+ const id = this.generatePrimaryKey(state);
133
+ const attrs = omit(state, this.primaryKey);
134
+
135
+ if (!id) {
71
136
  throw new Error('Cannot update or create without uniq index');
72
137
  }
73
138
 
74
139
  if (exist) {
75
- return this.update(state[this.uniqIndex], omit(state, [this.uniqIndex]), ctx);
140
+ return this.update(id, attrs, ctx);
76
141
  }
77
142
 
78
- return this.create(state[this.uniqIndex], omit(state, [this.uniqIndex]), ctx);
143
+ return this.create(id, attrs, ctx);
79
144
  }
80
145
 
81
146
  _reset() {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ocap/statedb-fs",
3
3
  "description": "OCAP statedb adapter that uses fs as backend",
4
- "version": "1.20.15",
4
+ "version": "1.21.0",
5
5
  "author": "wangshijun <shijun@arcblock.io> (https://www.arcblock.io)",
6
6
  "bugs": {
7
7
  "url": "https://github.com/ArcBlock/blockchain/issues",
@@ -36,9 +36,9 @@
36
36
  "debug": "^4.3.6",
37
37
  "lodash": "^4.17.21",
38
38
  "lokijs": "^1.5.12",
39
- "@ocap/state": "1.20.15",
40
- "@ocap/statedb": "1.20.15",
41
- "@ocap/util": "1.20.15"
39
+ "@ocap/state": "1.21.0",
40
+ "@ocap/util": "1.21.0",
41
+ "@ocap/statedb": "1.21.0"
42
42
  },
43
43
  "scripts": {
44
44
  "lint": "eslint tests lib",