Merge PR #896 from 'nodech/update-bidreveal-entries'
This commit is contained in:
commit
5294be7eb8
13 changed files with 976 additions and 25 deletions
15
CHANGELOG.md
15
CHANGELOG.md
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
## Unreleased
|
||||
|
||||
**When upgrading to this version of hsd, you must pass `--wallet-migrate=3` when
|
||||
**When upgrading to this version of hsd, you must pass `--wallet-migrate=4` when
|
||||
you run it for the first time.**
|
||||
|
||||
### Primitives
|
||||
|
|
@ -72,7 +72,18 @@ process and allows parallel rescans.
|
|||
conflicting transactions)
|
||||
- Add options to `getNames` for passing `own`.
|
||||
- Rename `createAuctionTxs` to `createAuctionTXs`.
|
||||
|
||||
- All `bid` serializations will include `height` of the bid. (`-1` if
|
||||
it was migrated not-owned bid)
|
||||
- `GET /wallet/:id/auction` (`getAuctions`)
|
||||
- `GET /wallet/:id/auction/:name` (`getAuctionByName`)
|
||||
- `GET /wallet/:id/bid` (`getBids`)
|
||||
- `GET /wallet/:id/bid/:name` (`getBidsByName`)
|
||||
- All `reveal` serializations will include `bidPrevout` of the bid. (`null` if
|
||||
it was migrated not-owned reveal)
|
||||
- `GET /wallet/:id/auction` (`getAuctions`)
|
||||
- `GET /wallet/:id/auction/:name` (`getAuctionByName`)
|
||||
- `GET /wallet/:id/reveal` (`getReveals`)
|
||||
- `GET /wallet/:id/reveal/:name` (`getRevealsByName`)
|
||||
|
||||
## v6.0.0
|
||||
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ exports.wdb = {
|
|||
* U[tx-hash] -> name undo record (name undo record by tx hash)
|
||||
* i[name-hash][tx-hash][index] -> bid (BlindBid by name + tx + index)
|
||||
* B[name-hash][tx-hash][index] -> reveal (BidReveal by name + tx + index)
|
||||
* E[name-hash][tx-hash][index] - bid to reveal out (by bid txhash + index)
|
||||
* v[blind-hash] -> blind (Blind Value by blind hash)
|
||||
* o[name-hash] -> tx hash OPEN only (tx hash by name hash)
|
||||
*/
|
||||
|
|
@ -163,6 +164,7 @@ exports.txdb = {
|
|||
U: bdb.key('U', ['hash256']),
|
||||
i: bdb.key('i', ['hash256', 'hash256', 'uint32']),
|
||||
B: bdb.key('B', ['hash256', 'hash256', 'uint32']),
|
||||
E: bdb.key('E', ['hash256', 'hash256', 'uint32']),
|
||||
v: bdb.key('v', ['hash256']),
|
||||
o: bdb.key('o', ['hash256'])
|
||||
};
|
||||
|
|
|
|||
|
|
@ -19,6 +19,8 @@ const WalletKey = require('./walletkey');
|
|||
const Path = require('./path');
|
||||
const Script = require('../script/script');
|
||||
const MapRecord = require('./records').MapRecord;
|
||||
const Outpoint = require('../primitives/outpoint');
|
||||
const TX = require('../primitives/tx');
|
||||
const AbstractMigration = require('../migrations/migration');
|
||||
const {
|
||||
MigrationResult,
|
||||
|
|
@ -27,6 +29,10 @@ const {
|
|||
oldLayout
|
||||
} = require('../migrations/migrator');
|
||||
const layouts = require('./layout');
|
||||
const wlayout = layouts.wdb;
|
||||
|
||||
/** @typedef {import('bdb').DB} DB */
|
||||
/** @typedef {import('./walletdb')} WalletDB */
|
||||
|
||||
/**
|
||||
* Switch to new migrations layout.
|
||||
|
|
@ -54,7 +60,7 @@ class MigrateMigrations extends AbstractMigration {
|
|||
|
||||
/**
|
||||
* Actual migration
|
||||
* @param {Batch} b
|
||||
* @param {DB.Batch} b
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
|
|
@ -468,6 +474,249 @@ class MigrateTXDBBalances extends AbstractMigration {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies to WalletDB v2
|
||||
* Migrate bid reveal entries.
|
||||
* - Adds height to the blind bid entries.
|
||||
* - NOTE: This can not be recovered if the bid is not owned by the wallet.
|
||||
* Wallet does not store transactions for not-owned bids.
|
||||
* - Add Bid Outpoint information to the reveal (BidReveal) entries.
|
||||
* - NOTE: This information can not be recovered for not-owned reveals.
|
||||
* Wallet does not store transactions for not-owned reveals.
|
||||
* - Add new BID -> REVEAL index. (layout.E)
|
||||
* - NOTE: This information can not be recovered for not-owned reveals.
|
||||
* Wallet does not store transactions for not-owned reveals.
|
||||
*
|
||||
*/
|
||||
|
||||
class MigrateBidRevealEntries extends AbstractMigration {
|
||||
/**
|
||||
* Create Bid Reveal Entries migration object.
|
||||
* @param {WalletMigratorOptions} options
|
||||
* @constructor
|
||||
*/
|
||||
|
||||
constructor(options) {
|
||||
super(options);
|
||||
|
||||
this.options = options;
|
||||
this.logger = options.logger.context('bid-reveal-entries-migration');
|
||||
this.db = options.db;
|
||||
this.ldb = options.ldb;
|
||||
this.layout = MigrateBidRevealEntries.layout();
|
||||
}
|
||||
|
||||
/**
|
||||
* We always migrate.
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
async check() {
|
||||
return types.MIGRATE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actual migration
|
||||
* @param {DB.Batch} b
|
||||
* @param {WalletMigrationResult} pending
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
async migrate(b, pending) {
|
||||
/** @type {Number[]} */
|
||||
const wids = await this.ldb.keys({
|
||||
gte: wlayout.W.min(),
|
||||
lte: wlayout.W.max(),
|
||||
parse: key => wlayout.W.decode(key)[0]
|
||||
});
|
||||
|
||||
for (const wid of wids) {
|
||||
await this.migrateReveals(wid);
|
||||
await this.migrateBids(wid);
|
||||
}
|
||||
|
||||
this.db.writeVersion(b, 3);
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate reveals and index Bid2Reveal
|
||||
* @param {Number} wid
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
async migrateReveals(wid) {
|
||||
const txlayout = this.layout.txdb;
|
||||
const prefix = txlayout.prefix.encode(wid);
|
||||
const bucket = this.ldb.bucket(prefix);
|
||||
const emptyOutpoint = new Outpoint();
|
||||
|
||||
const reveals = bucket.iterator({
|
||||
gte: txlayout.B.min(),
|
||||
lte: txlayout.B.max(),
|
||||
values: true
|
||||
});
|
||||
|
||||
for await (const {key, value} of reveals) {
|
||||
const b = bucket.batch();
|
||||
const [nameHash, txHash, txIndex] = txlayout.B.decode(key);
|
||||
const nameLen = value[0];
|
||||
const totalOld = nameLen + 1 + 13;
|
||||
const totalNew = nameLen + 1 + 13 + 36;
|
||||
|
||||
// allow migration to be interrupted in the middle.
|
||||
assert(value.length === totalOld || value.length === totalNew);
|
||||
|
||||
// skip if already migrated.
|
||||
if (value.length === totalNew)
|
||||
continue;
|
||||
|
||||
const owned = value[nameLen + 1 + 12];
|
||||
const rawTXRecord = await bucket.get(txlayout.t.encode(txHash));
|
||||
assert(owned && rawTXRecord || !owned);
|
||||
|
||||
// We can not index the bid link and bid2reveal index if
|
||||
// the transaction is not owned by the wallet.
|
||||
// But we need to put null outpoint to the reveal for serialization.
|
||||
if (!owned) {
|
||||
const newReveal = Buffer.concat([value, emptyOutpoint.encode()]);
|
||||
assert(newReveal.length === totalNew);
|
||||
b.put(key, newReveal);
|
||||
await b.write();
|
||||
continue;
|
||||
}
|
||||
|
||||
const reader = bio.read(rawTXRecord);
|
||||
const tx = TX.fromReader(reader);
|
||||
assert(tx.inputs[txIndex]);
|
||||
|
||||
const bidPrevout = tx.inputs[txIndex].prevout;
|
||||
const bidKey = txlayout.i.encode(
|
||||
nameHash, bidPrevout.hash, bidPrevout.index);
|
||||
const bidRecord = await bucket.get(bidKey);
|
||||
// ensure bid exists.
|
||||
assert(bidRecord);
|
||||
|
||||
const newReveal = Buffer.concat([value, bidPrevout.encode()]);
|
||||
assert(newReveal.length === totalNew);
|
||||
// update reveal with bid outpoint.
|
||||
b.put(key, newReveal);
|
||||
// index bid to reveal.
|
||||
b.put(txlayout.E.encode(nameHash, bidPrevout.hash, bidPrevout.index),
|
||||
(new Outpoint(txHash, txIndex)).encode());
|
||||
await b.write();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate bids, add height to the entries.
|
||||
* @param {Number} wid
|
||||
* @returns {Promise}
|
||||
*/
|
||||
|
||||
async migrateBids(wid) {
|
||||
const txlayout = this.layout.txdb;
|
||||
const prefix = txlayout.prefix.encode(wid);
|
||||
const bucket = this.ldb.bucket(prefix);
|
||||
|
||||
const bids = bucket.iterator({
|
||||
gte: txlayout.i.min(),
|
||||
lte: txlayout.i.max(),
|
||||
values: true
|
||||
});
|
||||
|
||||
/**
|
||||
* @param {Buffer} blindBid
|
||||
* @param {Number} height
|
||||
* @returns {Buffer}
|
||||
*/
|
||||
|
||||
const reencodeBlindBid = (blindBid, height) => {
|
||||
const nameLen = blindBid[0];
|
||||
const totalOld = nameLen + 1 + 41;
|
||||
const totalNew = nameLen + 1 + 41 + 4;
|
||||
assert(blindBid.length === totalOld);
|
||||
|
||||
const newBlindBid = Buffer.alloc(totalNew);
|
||||
// copy everything before expected height place.
|
||||
blindBid.copy(newBlindBid, 0, 0, totalOld - 1);
|
||||
// copy height.
|
||||
bio.encoding.writeU32(newBlindBid, height, totalOld - 1);
|
||||
// copy last byte (owned flag).
|
||||
blindBid.copy(newBlindBid, totalNew - 1, totalOld - 1);
|
||||
|
||||
return newBlindBid;
|
||||
};
|
||||
|
||||
for await (const {key, value} of bids) {
|
||||
const b = bucket.batch();
|
||||
const [,txHash] = txlayout.i.decode(key);
|
||||
const nameLen = value[0];
|
||||
const totalNew = nameLen + 1 + 41 + 4;
|
||||
|
||||
// allow migration to be interrupted in the middle.
|
||||
if (totalNew === value.length)
|
||||
continue;
|
||||
|
||||
const owned = value[nameLen + 1 + 40];
|
||||
if (!owned) {
|
||||
const height = 0xffffffff; // -1
|
||||
const newValue = reencodeBlindBid(value, height);
|
||||
b.put(key, newValue);
|
||||
await b.write();
|
||||
continue;
|
||||
}
|
||||
|
||||
const rawTXRecord = await bucket.get(txlayout.t.encode(txHash));
|
||||
assert(rawTXRecord);
|
||||
|
||||
const br = bio.read(rawTXRecord);
|
||||
TX.fromReader(br);
|
||||
// skip mtime.
|
||||
br.seek(4);
|
||||
|
||||
const hasBlock = br.readU8() === 1;
|
||||
// We only index the bid in blocks, not in mempool.
|
||||
assert(hasBlock);
|
||||
|
||||
// skip hash.
|
||||
br.seek(32);
|
||||
const height = br.readU32();
|
||||
const newValue = reencodeBlindBid(value, height);
|
||||
b.put(key, newValue);
|
||||
|
||||
await b.write();
|
||||
}
|
||||
}
|
||||
|
||||
static info() {
|
||||
return {
|
||||
name: 'Bid reveal entries migration',
|
||||
description: 'Migrate bids and reveals to link each other.'
|
||||
};
|
||||
}
|
||||
|
||||
static layout() {
|
||||
return {
|
||||
wdb: {
|
||||
V: bdb.key('V'),
|
||||
// W[wid] -> wallet id
|
||||
W: bdb.key('W', ['uint32'])
|
||||
},
|
||||
txdb: {
|
||||
prefix: bdb.key('t', ['uint32']),
|
||||
// t[tx-hash] -> extended tx (Read only)
|
||||
t: bdb.key('t', ['hash256']),
|
||||
// i[name-hash][tx-hash][index] -> txdb.BlindBid
|
||||
i: bdb.key('i', ['hash256', 'hash256', 'uint32']),
|
||||
// B[name-hash][tx-hash][index] -> txdb.BidReveal
|
||||
B: bdb.key('B', ['hash256', 'hash256', 'uint32']),
|
||||
// E[name-hash][tx-hash][index] -> bid to reveal out.
|
||||
E: bdb.key('E', ['hash256', 'hash256', 'uint32'])
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wallet migration results.
|
||||
* @alias module:blockchain.WalletMigrationResult
|
||||
|
|
@ -548,12 +797,14 @@ class WalletMigratorOptions {
|
|||
this.migrateFlag = -1;
|
||||
|
||||
this.dbVersion = 0;
|
||||
/** @type {WalletDB} */
|
||||
this.db = null;
|
||||
/** @type {DB} */
|
||||
this.ldb = null;
|
||||
this.layout = layouts.wdb;
|
||||
|
||||
if (options)
|
||||
this.fromOptions(options);
|
||||
assert(options);
|
||||
this.fromOptions(options);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -603,7 +854,8 @@ exports.migrations = {
|
|||
0: MigrateMigrations,
|
||||
1: MigrateChangeAddress,
|
||||
2: MigrateAccountLookahead,
|
||||
3: MigrateTXDBBalances
|
||||
3: MigrateTXDBBalances,
|
||||
4: MigrateBidRevealEntries
|
||||
};
|
||||
|
||||
// Expose migrations
|
||||
|
|
@ -611,5 +863,6 @@ exports.MigrateChangeAddress = MigrateChangeAddress;
|
|||
exports.MigrateMigrations = MigrateMigrations;
|
||||
exports.MigrateAccountLookahead = MigrateAccountLookahead;
|
||||
exports.MigrateTXDBBalances = MigrateTXDBBalances;
|
||||
exports.MigrateBidRevealEntries = MigrateBidRevealEntries;
|
||||
|
||||
module.exports = exports;
|
||||
|
|
|
|||
|
|
@ -550,6 +550,7 @@ class TXDB {
|
|||
bb.lockup = options.lockup;
|
||||
bb.blind = options.blind;
|
||||
bb.own = options.own;
|
||||
bb.height = options.height;
|
||||
b.put(layout.i.encode(nameHash, hash, index), bb.encode());
|
||||
}
|
||||
|
||||
|
|
@ -646,23 +647,67 @@ class TXDB {
|
|||
return brv;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reveal by bid outpoint.
|
||||
* @param {Buffer} nameHash
|
||||
* @param {Outpoint} bidOut
|
||||
* @returns {Promise<BidReveal?>}
|
||||
*/
|
||||
|
||||
async getRevealByBid(nameHash, bidOut) {
|
||||
const rawOutpoint = await this.bucket.get(
|
||||
layout.E.encode(nameHash, bidOut.hash, bidOut.index));
|
||||
|
||||
if (!rawOutpoint)
|
||||
return null;
|
||||
|
||||
const outpoint = Outpoint.decode(rawOutpoint);
|
||||
return this.getReveal(nameHash, outpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bid by reveal outpoint.
|
||||
* @param {Buffer} nameHash
|
||||
* @param {Outpoint} revealOut
|
||||
* @returns {Promise<BlindBid?>}
|
||||
*/
|
||||
|
||||
async getBidByReveal(nameHash, revealOut) {
|
||||
const reveal = await this.getReveal(nameHash, revealOut);
|
||||
|
||||
if (!reveal)
|
||||
return null;
|
||||
|
||||
return this.getBid(nameHash, reveal.bidPrevout);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a reveal.
|
||||
* @param {Object} b
|
||||
* @param {Buffer} nameHash
|
||||
* @param {Outpoint} outpoint
|
||||
* @param {Object} options
|
||||
* @param {String} options.name
|
||||
* @param {Amount} options.value
|
||||
* @param {Number} options.height
|
||||
* @param {Boolean} options.own
|
||||
* @param {Outpoint} options.bidPrevout
|
||||
* @returns {void}
|
||||
*/
|
||||
|
||||
putReveal(b, nameHash, outpoint, options) {
|
||||
const {hash, index} = outpoint;
|
||||
const {bidPrevout} = options;
|
||||
const brv = new BidReveal();
|
||||
brv.nameHash = nameHash;
|
||||
brv.name = options.name;
|
||||
brv.value = options.value;
|
||||
brv.height = options.height;
|
||||
brv.own = options.own;
|
||||
brv.bidPrevout = bidPrevout;
|
||||
b.put(layout.B.encode(nameHash, hash, index), brv.encode());
|
||||
b.put(layout.E.encode(nameHash, bidPrevout.hash, bidPrevout.index),
|
||||
outpoint.encode());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -670,11 +715,13 @@ class TXDB {
|
|||
* @param {Object} b
|
||||
* @param {Buffer} nameHash
|
||||
* @param {Outpoint} outpoint
|
||||
* @param {Outpoint} bidPrevout
|
||||
*/
|
||||
|
||||
removeReveal(b, nameHash, outpoint) {
|
||||
removeReveal(b, nameHash, outpoint, bidPrevout) {
|
||||
const {hash, index} = outpoint;
|
||||
b.del(layout.B.encode(nameHash, hash, index));
|
||||
b.del(layout.E.encode(nameHash, bidPrevout.hash, bidPrevout.index));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -1975,6 +2022,7 @@ class TXDB {
|
|||
name,
|
||||
lockup,
|
||||
blind,
|
||||
height,
|
||||
own: false
|
||||
});
|
||||
|
||||
|
|
@ -1992,6 +2040,7 @@ class TXDB {
|
|||
name,
|
||||
lockup,
|
||||
blind,
|
||||
height,
|
||||
own: true
|
||||
});
|
||||
|
||||
|
|
@ -2012,18 +2061,20 @@ class TXDB {
|
|||
ns.setValue(output.value);
|
||||
}
|
||||
|
||||
const {prevout} = tx.inputs[i];
|
||||
|
||||
if (!path) {
|
||||
this.putReveal(b, nameHash, outpoint, {
|
||||
name: ns.name,
|
||||
value: output.value,
|
||||
height: height,
|
||||
own: false
|
||||
own: false,
|
||||
bidPrevout: prevout
|
||||
});
|
||||
updated = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const {prevout} = tx.inputs[i];
|
||||
const coin = view.getOutput(prevout);
|
||||
|
||||
if (coin) {
|
||||
|
|
@ -2041,7 +2092,8 @@ class TXDB {
|
|||
name: ns.name,
|
||||
value: output.value,
|
||||
height: height,
|
||||
own: true
|
||||
own: true,
|
||||
bidPrevout: prevout
|
||||
});
|
||||
|
||||
updated = true;
|
||||
|
|
@ -2225,8 +2277,9 @@ class TXDB {
|
|||
break;
|
||||
}
|
||||
case types.REVEAL: {
|
||||
const input = tx.inputs[i];
|
||||
const nameHash = covenant.getHash(0);
|
||||
this.removeReveal(b, nameHash, tx.outpoint(i));
|
||||
this.removeReveal(b, nameHash, tx.outpoint(i), input.prevout);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -3960,18 +4013,25 @@ class BlindBid extends bio.Struct {
|
|||
this.value = -1;
|
||||
this.lockup = 0;
|
||||
this.blind = consensus.ZERO_HASH;
|
||||
this.height = -1;
|
||||
this.own = false;
|
||||
}
|
||||
|
||||
getSize() {
|
||||
return 1 + this.name.length + 41;
|
||||
return 1 + this.name.length + 45;
|
||||
}
|
||||
|
||||
write(bw) {
|
||||
let height = this.height;
|
||||
|
||||
if (height === -1)
|
||||
height = 0xffffffff;
|
||||
|
||||
bw.writeU8(this.name.length);
|
||||
bw.writeBytes(this.name);
|
||||
bw.writeU64(this.lockup);
|
||||
bw.writeBytes(this.blind);
|
||||
bw.writeU32(height);
|
||||
bw.writeU8(this.own ? 1 : 0);
|
||||
return bw;
|
||||
}
|
||||
|
|
@ -3980,7 +4040,12 @@ class BlindBid extends bio.Struct {
|
|||
this.name = br.readBytes(br.readU8());
|
||||
this.lockup = br.readU64();
|
||||
this.blind = br.readBytes(32);
|
||||
this.height = br.readU32();
|
||||
this.own = br.readU8() === 1;
|
||||
|
||||
if (this.height === 0xffffffff)
|
||||
this.height = -1;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
@ -3992,6 +4057,7 @@ class BlindBid extends bio.Struct {
|
|||
value: this.value === -1 ? undefined : this.value,
|
||||
lockup: this.lockup,
|
||||
blind: this.blind.toString('hex'),
|
||||
height: this.height,
|
||||
own: this.own
|
||||
};
|
||||
}
|
||||
|
|
@ -4042,13 +4108,14 @@ class BidReveal extends bio.Struct {
|
|||
this.name = EMPTY;
|
||||
this.nameHash = consensus.ZERO_HASH;
|
||||
this.prevout = new Outpoint();
|
||||
this.bidPrevout = new Outpoint();
|
||||
this.value = 0;
|
||||
this.height = -1;
|
||||
this.own = false;
|
||||
}
|
||||
|
||||
getSize() {
|
||||
return 1 + this.name.length + 13;
|
||||
return 1 + this.name.length + 13 + 36;
|
||||
}
|
||||
|
||||
write(bw) {
|
||||
|
|
@ -4062,6 +4129,7 @@ class BidReveal extends bio.Struct {
|
|||
bw.writeU64(this.value);
|
||||
bw.writeU32(height);
|
||||
bw.writeU8(this.own ? 1 : 0);
|
||||
this.bidPrevout.write(bw);
|
||||
|
||||
return bw;
|
||||
}
|
||||
|
|
@ -4071,6 +4139,7 @@ class BidReveal extends bio.Struct {
|
|||
this.value = br.readU64();
|
||||
this.height = br.readU32();
|
||||
this.own = br.readU8() === 1;
|
||||
this.bidPrevout.read(br);
|
||||
|
||||
if (this.height === 0xffffffff)
|
||||
this.height = -1;
|
||||
|
|
@ -4083,6 +4152,7 @@ class BidReveal extends bio.Struct {
|
|||
name: this.name.toString('ascii'),
|
||||
nameHash: this.nameHash.toString('hex'),
|
||||
prevout: this.prevout.toJSON(),
|
||||
bidPrevout: this.bidPrevout.isNull() ? null : this.bidPrevout.toJSON(),
|
||||
value: this.value,
|
||||
height: this.height,
|
||||
own: this.own
|
||||
|
|
|
|||
|
|
@ -4700,6 +4700,17 @@ class Wallet extends EventEmitter {
|
|||
return this.txdb.getBlind(blind);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bid
|
||||
* @param {Buffer} nameHash
|
||||
* @param {Outpoint} outpoint
|
||||
* @returns {Promise<BlindBid?>}
|
||||
*/
|
||||
|
||||
async getBid(nameHash, outpoint) {
|
||||
return this.txdb.getBid(nameHash, outpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all bids for name.
|
||||
* @param {Buffer} nameHash
|
||||
|
|
@ -4720,6 +4731,28 @@ class Wallet extends EventEmitter {
|
|||
return this.txdb.getBids(name ? rules.hashName(name) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get bid by reveal.
|
||||
* @param {Buffer} nameHash
|
||||
* @param {Outpoint} outpoint - reveal outpoint
|
||||
* @returns {Promise<BlindBid?>}
|
||||
*/
|
||||
|
||||
async getBidByReveal(nameHash, outpoint) {
|
||||
return this.txdb.getBidByReveal(nameHash, outpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reveal.
|
||||
* @param {Buffer} nameHash
|
||||
* @param {Outpoint} outpoint
|
||||
* @returns {BidReveal?}
|
||||
*/
|
||||
|
||||
async getReveal(nameHash, outpoint) {
|
||||
return this.txdb.getReveal(nameHash, outpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all reveals by name.
|
||||
* @param {Buffer} nameHash
|
||||
|
|
@ -4740,6 +4773,17 @@ class Wallet extends EventEmitter {
|
|||
return this.txdb.getReveals(name ? rules.hashName(name) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get reveal for bid.
|
||||
* @param {Buffer} nameHash
|
||||
* @param {Outpoint} outpoint - bid outpoint
|
||||
* @returns {Promise<BlindReveal?>}
|
||||
*/
|
||||
|
||||
async getRevealByBid(nameHash, outpoint) {
|
||||
return this.txdb.getRevealByBid(nameHash, outpoint);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a transaction to the wallets TX history.
|
||||
* @param {TX} tx
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ class WalletDB extends EventEmitter {
|
|||
this.feeRate = this.options.feeRate;
|
||||
this.db = bdb.create(this.options);
|
||||
this.name = 'wallet';
|
||||
this.version = 2;
|
||||
this.version = 3;
|
||||
|
||||
// chain state.
|
||||
this.hasStateCache = false;
|
||||
|
|
|
|||
11
test/data/migrations/README.md
Normal file
11
test/data/migrations/README.md
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
Migration data with before/after entries.
|
||||
|
||||
It contains generator scripts and manually assembled jsons for the migrations.
|
||||
|
||||
## Notes
|
||||
- wallet-4-bid-reveal.json (wallet-4-bid-reveal-gen.js)
|
||||
- `fullAfter` is db dump with new version, `after` is filtered out things
|
||||
that can not be recovered.
|
||||
- Removed not-owned BID <-> Reveal bidings, they are not possible to
|
||||
recover. We don't have transactions for those. We can't recover height of
|
||||
the bids either.
|
||||
186
test/data/migrations/wallet-4-bid-reveal-gen.js
Normal file
186
test/data/migrations/wallet-4-bid-reveal-gen.js
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
'use strict';
|
||||
|
||||
// Works for walletdb version 2 to 3 migration.
|
||||
// HSD v6 -> v7 migration.
|
||||
|
||||
const assert = require('bsert');
|
||||
const bdb = require('bdb');
|
||||
const Network = require('../../../lib/protocol/network');
|
||||
const WalletDB = require('../../../lib/wallet/walletdb');
|
||||
const MTX = require('../../../lib/primitives/mtx');
|
||||
const wutils = require('../../../test/util/wallet');
|
||||
const rules = require('../../../lib/covenants/rules');
|
||||
|
||||
const layout = {
|
||||
wdb: {
|
||||
V: bdb.key('V'),
|
||||
// W[wid] -> wallet id
|
||||
W: bdb.key('W', ['uint32'])
|
||||
},
|
||||
txdb: {
|
||||
prefix: bdb.key('t', ['uint32']),
|
||||
// t[tx-hash] -> extended tx (Read only)
|
||||
t: bdb.key('t', ['hash256']),
|
||||
// i[name-hash][tx-hash][index] -> txdb.BlindBid
|
||||
i: bdb.key('i', ['hash256', 'hash256', 'uint32']),
|
||||
// B[name-hash][tx-hash][index] -> txdb.BidReveal
|
||||
B: bdb.key('B', ['hash256', 'hash256', 'uint32']),
|
||||
// E[name-hash][tx-hash][index] -> bid to reveal out.
|
||||
E: bdb.key('E', ['hash256', 'hash256', 'uint32'])
|
||||
}
|
||||
};
|
||||
|
||||
const NETWORK = Network.get('regtest');
|
||||
|
||||
const wallet1priv = 'rprvKE8qsHtkmUxUSPQdn2sFKFUcKyUQz9pKQhxjEWecnXg9hgJMsmJXcw'
|
||||
+ 'J77SqmHT1R6mcuNqVPzgT2EoGStsXaUN92VJKhQWUB6uZdL8gAZvez';
|
||||
const wallet2priv = 'rprvKE8qsHtkmUxUSR4jE7Lti9XV77hv7xxacAShw5MvxY6RfsAYVeB1WL'
|
||||
+ 'WtjiebDmqTruVJxmMeQUMkk61e83WDZbZidDnNPhHyQpeEwxjuSZuG';
|
||||
|
||||
let txID = 0;
|
||||
let timeCounter = 0;
|
||||
|
||||
(async () => {
|
||||
const wdb = new WalletDB({
|
||||
network: NETWORK,
|
||||
memory: true,
|
||||
nowFn: () => timeCounter++
|
||||
});
|
||||
|
||||
await wdb.open();
|
||||
|
||||
const wallet1 = await wdb.create({
|
||||
id: 'wallet1',
|
||||
master: wallet1priv
|
||||
});
|
||||
|
||||
await wallet1.createAccount('alt');
|
||||
|
||||
const wallet2 = await wdb.create({
|
||||
id: 'wallet2',
|
||||
master: wallet2priv
|
||||
});
|
||||
|
||||
// add 10 blocks to the wallet.
|
||||
await mineBlocks(wdb, 100);
|
||||
|
||||
// fund wallets
|
||||
const mtx1 = new MTX();
|
||||
mtx1.addInput(wutils.deterministicInput(txID++));
|
||||
mtx1.addOutput(await wallet1.receiveAddress(0), 10e6);
|
||||
|
||||
const mtx2 = new MTX();
|
||||
mtx2.addInput(wutils.deterministicInput(txID++));
|
||||
mtx2.addOutput(await wallet1.receiveAddress(1), 10e6);
|
||||
|
||||
// fund second wallet.
|
||||
const mtx3 = new MTX();
|
||||
mtx3.addInput(wutils.deterministicInput(txID++));
|
||||
mtx3.addOutput(await wallet2.receiveAddress(), 10e6);
|
||||
|
||||
await wdb.addBlock(wutils.nextEntry(wdb), [
|
||||
mtx1.toTX(),
|
||||
mtx2.toTX(),
|
||||
mtx3.toTX()
|
||||
]);
|
||||
|
||||
const name1 = 'testname1';
|
||||
const name2 = 'testname2';
|
||||
|
||||
const open1 = await wallet1.createOpen(name1, {
|
||||
account: 0
|
||||
});
|
||||
await wdb.addTX(open1.toTX());
|
||||
const open2 = await wallet1.createOpen(name2, {
|
||||
account: 0
|
||||
});
|
||||
|
||||
await wdb.addBlock(wutils.nextEntry(wdb), [
|
||||
open1.toTX(),
|
||||
open2.toTX()
|
||||
]);
|
||||
|
||||
await mineBlocks(wdb, NETWORK.names.treeInterval + 1);
|
||||
|
||||
const ns = await wallet1.getNameState(rules.hashName(name1));
|
||||
const bid1 = await wallet1.createBid(name1, 2e6, 2e6, {
|
||||
account: 0
|
||||
});
|
||||
|
||||
const bid2 = await wallet1.createBid(name2, 2e6, 2e6, {
|
||||
account: 1
|
||||
});
|
||||
|
||||
// wallet2 does not know the state of the name.
|
||||
const _getNameStatusBak = wdb.getNameStatus;
|
||||
wdb.getNameStatus = (nameHash) => {
|
||||
assert(Buffer.isBuffer(nameHash));
|
||||
assert(nameHash.equals(rules.hashName(name1)));
|
||||
|
||||
return ns;
|
||||
};
|
||||
|
||||
const bid3 = await wallet2.createBid(name1, 3e6, 3e6);
|
||||
await wdb.addBlock(wutils.nextEntry(wdb), [
|
||||
bid1.toTX(),
|
||||
bid2.toTX(),
|
||||
bid3.toTX()
|
||||
]);
|
||||
|
||||
wdb.getNameStatus = _getNameStatusBak;
|
||||
await mineBlocks(wdb, NETWORK.names.biddingPeriod);
|
||||
|
||||
const reveal1 = await wallet1.createReveal(name1, {
|
||||
account: 0
|
||||
});
|
||||
|
||||
const reveal2 = await wallet1.createReveal(name2, {
|
||||
account: 1
|
||||
});
|
||||
|
||||
const reveal3 = await wallet2.createReveal(name1);
|
||||
|
||||
await wdb.addBlock(wutils.nextEntry(wdb), [
|
||||
reveal1.toTX(),
|
||||
reveal2.toTX(),
|
||||
reveal3.toTX()
|
||||
]);
|
||||
|
||||
const dump = await getMigrationDump(wdb);
|
||||
console.log(JSON.stringify({
|
||||
data: dump
|
||||
}, null, 2));
|
||||
|
||||
await wdb.close();
|
||||
})().catch((e) => {
|
||||
console.error(e.stack);
|
||||
process.exit(1);
|
||||
});
|
||||
|
||||
async function mineBlocks(wdb, n) {
|
||||
for (let i = 0; i < n; i++) {
|
||||
const entry = wutils.nextEntry(wdb);
|
||||
await wdb.addBlock(entry, []);
|
||||
}
|
||||
};
|
||||
|
||||
async function getMigrationDump(wdb) {
|
||||
const prefixes = [];
|
||||
|
||||
for (let i = 1; i < 3; i++) {
|
||||
const tprefix = layout.txdb.prefix.encode(i).toString('hex');
|
||||
const ti = tprefix + 'i'.charCodeAt(0).toString(16);
|
||||
const tB = tprefix + 'B'.charCodeAt(0).toString(16);
|
||||
const tE = tprefix + 'E'.charCodeAt(0).toString(16);
|
||||
const tt = tprefix + 't'.charCodeAt(0).toString(16);
|
||||
prefixes.push(ti, tB, tE, tt);
|
||||
}
|
||||
|
||||
for (let i = 0; i < 3; i++) {
|
||||
prefixes.push(layout.wdb.W.encode(i).toString('hex'));
|
||||
}
|
||||
|
||||
const dump = await wutils.dumpWDB(wdb, prefixes);
|
||||
|
||||
return dump;
|
||||
};
|
||||
86
test/data/migrations/wallet-4-bid-reveal.json
Normal file
86
test/data/migrations/wallet-4-bid-reveal.json
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
{
|
||||
"description": "Migration for linking bid to reveal and vice versa. Affects txdb layout i, B and E (new). Uses txdb layout t to collect data.",
|
||||
"before": {
|
||||
"5700000000": "077072696d617279",
|
||||
"5700000001": "0777616c6c657431",
|
||||
"5700000002": "0777616c6c657432",
|
||||
"740000000142074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382ccc9c76cf691cb577640a511a7e1539e72f5672ea75bf364dd3d2e6add1e10efd00000000": "09746573746e616d653280841e00000000007300000001",
|
||||
"740000000142aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000": "09746573746e616d6531c0c62d00000000007300000000",
|
||||
"740000000142aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000": "09746573746e616d653180841e00000000007300000001",
|
||||
"740000000169074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c1ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000": "09746573746e616d653280841e0000000000d9d0782ee039b29bccf72e07f6a8d0cf4aea50ff9ee1764d762f913f5e14980b01",
|
||||
"740000000169aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "09746573746e616d6531c0c62d0000000000ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d400",
|
||||
"740000000169aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000": "09746573746e616d653180841e0000000000fb278d451ca7cf961fece75487723f06d66fbc7326e282b1889a1757066b890501",
|
||||
"7400000001741706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f4": "00000000029a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000ffffffff9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849101000000ffffffff0280841e000000000000142f6d31b494bdfa42034719ca28894496cdb89359040320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000020450566fda05dda024354f47c1f6d07769342a57c5dffe1d3a908a9043a83156bb8cd79000000000000149ae567da742bcd8894e5f0c789aa1a5a017396cc000000000000020021037f44c97b1d5c69af830f7c14647c710e95300bd94dd92ad75fdd30ca7489287f020021024e3c15df14b521891f269af40258119698ebaed5f7299abf9583546e2438e73209000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f",
|
||||
"7400000001741b474b762714c2f0adb18d2127d4f32fd8202d64f4f2a031195cb72a98a8f25a": "0000000001a1f71206f2277bacd0b1fa32a633d20b69deb3e6dc1bc95db78a572d9f30915501000000ffffffff02000000000000000000146a94c2244bd9a4b5257a8c946b68af72f4118b35020320074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c040000000009746573746e616d6532207998000000000000141fcdaa5bd5efa8811106f6390e5ecc327083426b0000000000000200210256bc84ba99364f4a0146c1203b52104447d1085aee74df1242be003664e36cdf0400000001827ba7677abf30338299f8d2ab3c1a0a1144544dac46f5ec486bac54d3a9fe1c660000001054ce1dffffff7f",
|
||||
"7400000001741ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a746": "0000000001a76270b8188bbdd60aba522a75eb9643fb12183f9e7a929abc5fb7c0bdfc270200000000ffffffff0280841e000000000000146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54030420074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c046600000009746573746e616d653220d9d0782ee039b29bccf72e07f6a8d0cf4aea50ff9ee1764d762f913f5e14980bbc007a00000000000014f0f269d0cb5eb1e065b1084d1d787a7ec5b6ed8c00000000000002002102df1a2c03a8d1b89b0b2f570ad99764f7dc4bcc710afa9745af65f2bc7789cfc806000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f",
|
||||
"7400000001742bca389fe3871c49fe6ea6706980a247fa4c4e97e60e1ac79598bd151b8055f3": "000000000111da6d1f761ddf9bdb4c9d6e5303ebd41f61858d0a5647a1a7bfe089bf921be900000000ffffffff0180969800000000000014cffd1219803f5f43f72a2a8e841a2fefb0347657000000000000000000000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f",
|
||||
"7400000001749a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d8491": "00000000011b474b762714c2f0adb18d2127d4f32fd8202d64f4f2a031195cb72a98a8f25a01000000ffffffff0280841e000000000000142f6d31b494bdfa42034719ca28894496cdb89359030420aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000009746573746e616d653120fb278d451ca7cf961fece75487723f06d66fbc7326e282b1889a1757066b89055ce37900000000000014222740511246fe2b10957049ae10e0bbe7fa996c000000000000020021023960f57b3d5844794ac0dd5576fb59b3a64d82d4d76285a36173d169412539e205000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f",
|
||||
"740000000174a1f71206f2277bacd0b1fa32a633d20b69deb3e6dc1bc95db78a572d9f309155": "00000000012bca389fe3871c49fe6ea6706980a247fa4c4e97e60e1ac79598bd151b8055f300000000ffffffff0200000000000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa3020320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a040000000009746573746e616d6531d0879800000000000014a62c71694f8bf7583bfe73bd1b2235b81ee12558000000000000020021020c30819abd60558d2f9d1fdfabb5d46df7254b5a9346c9d78dc9b5ace6b1089e0300000001827ba7677abf30338299f8d2ab3c1a0a1144544dac46f5ec486bac54d3a9fe1c660000001054ce1dffffff7f",
|
||||
"740000000174a76270b8188bbdd60aba522a75eb9643fb12183f9e7a929abc5fb7c0bdfc2702": "0000000001e12c22d4f162d9a012c9319233da5d3e923cc5e1029b8f90e47249c9ab256b3500000000ffffffff0180969800000000000014657eb0831fd4fafd3e595163416d76861ea9acd9000000000000000100000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f",
|
||||
"740000000174cc9c76cf691cb577640a511a7e1539e72f5672ea75bf364dd3d2e6add1e10efd": "00000000021ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000ffffffff1ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74601000000ffffffff0280841e000000000000146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54040320074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c046600000020f70095f015fc11f5bae44f6136cbe1bb846103e79e065ea54f9e56f58e5322ab18eb79000000000000145258eb6f21709d00f66fa39deab6e8d5391eae5f00000000000002002103d1ab347d75498ccde90f26907c8aad98fcb5ecc922ff7de8f4be83c9b7a099b6020021021e772405b9b642a904d6e1f5739b56d1714327853f6cbb97777c70ef0c54c5450b000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f",
|
||||
"740000000242aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000": "09746573746e616d6531c0c62d00000000007300000001",
|
||||
"740000000242aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000": "09746573746e616d653180841e00000000007300000000",
|
||||
"740000000269aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "09746573746e616d6531c0c62d0000000000ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d401",
|
||||
"74000000027401f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf": "00000000023bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000ffffffff3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d901000000ffffffff02c0c62d000000000000148e659d46d7ceb9464cc04f3aeb73bc2702c1e7a3040320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a0466000000204c679195282ea315863be7119a2f1f6a4b9486fe7f86e6e778435f4fe9ed4083d8a86a00000000000014bfe57d3d51c357a99aa9c84195fe8e000273399a00000000000002002103aa28c5dc394dd41bda11c296f1e0ef98edfa09e5730b6abdab5f1e1e700e2a61020021036b182787487be70c07012724b985e8fe7f49067f2a3e4c5535da0118a21d13fa0c000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f",
|
||||
"7400000002743bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d9": "0000000001abe815e307b0d125b21242058b6e0a8a43190c86456af1de27c17f0c8ddc29a600000000ffffffff02c0c62d000000000000148e659d46d7ceb9464cc04f3aeb73bc2702c1e7a3030420aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000009746573746e616d653120ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d47cbe6a00000000000014b44aae19323f53e63e5cd184dba19a71616808150000000000000200210237b59c5644219778989265c96ecdb213578feca83fc908a975d8465359779e8807000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f",
|
||||
"740000000274abe815e307b0d125b21242058b6e0a8a43190c86456af1de27c17f0c8ddc29a6": "00000000017b0aa1735e5ba58d3236316c671fe4f00ed366ee72417c9ed02a53a8019e85b800000000ffffffff01809698000000000000148ec33e2b7a9dc5fde7e6a166005d3b29265b313d000000000000000200000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f"
|
||||
},
|
||||
"fullAfter": {
|
||||
"5700000000": "077072696d617279",
|
||||
"5700000001": "0777616c6c657431",
|
||||
"5700000002": "0777616c6c657432",
|
||||
"740000000142074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382ccc9c76cf691cb577640a511a7e1539e72f5672ea75bf364dd3d2e6add1e10efd00000000": "09746573746e616d653280841e000000000073000000011ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000",
|
||||
"740000000142aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000": "09746573746e616d6531c0c62d000000000073000000003bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000",
|
||||
"740000000142aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000": "09746573746e616d653180841e000000000073000000019a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000",
|
||||
"740000000145074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c1ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000": "cc9c76cf691cb577640a511a7e1539e72f5672ea75bf364dd3d2e6add1e10efd00000000",
|
||||
"740000000145aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000",
|
||||
"740000000145aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000": "1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000",
|
||||
"740000000169074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c1ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000": "09746573746e616d653280841e0000000000d9d0782ee039b29bccf72e07f6a8d0cf4aea50ff9ee1764d762f913f5e14980b6d00000001",
|
||||
"740000000169aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "09746573746e616d6531c0c62d0000000000ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d46d00000000",
|
||||
"740000000169aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000": "09746573746e616d653180841e0000000000fb278d451ca7cf961fece75487723f06d66fbc7326e282b1889a1757066b89056d00000001",
|
||||
"7400000001741706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f4": "00000000029a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000ffffffff9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849101000000ffffffff0280841e000000000000142f6d31b494bdfa42034719ca28894496cdb89359040320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000020450566fda05dda024354f47c1f6d07769342a57c5dffe1d3a908a9043a83156bb8cd79000000000000149ae567da742bcd8894e5f0c789aa1a5a017396cc000000000000020021037f44c97b1d5c69af830f7c14647c710e95300bd94dd92ad75fdd30ca7489287f020021024e3c15df14b521891f269af40258119698ebaed5f7299abf9583546e2438e73209000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f",
|
||||
"7400000001741b474b762714c2f0adb18d2127d4f32fd8202d64f4f2a031195cb72a98a8f25a": "0000000001a1f71206f2277bacd0b1fa32a633d20b69deb3e6dc1bc95db78a572d9f30915501000000ffffffff02000000000000000000146a94c2244bd9a4b5257a8c946b68af72f4118b35020320074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c040000000009746573746e616d6532207998000000000000141fcdaa5bd5efa8811106f6390e5ecc327083426b0000000000000200210256bc84ba99364f4a0146c1203b52104447d1085aee74df1242be003664e36cdf0400000001827ba7677abf30338299f8d2ab3c1a0a1144544dac46f5ec486bac54d3a9fe1c660000001054ce1dffffff7f",
|
||||
"7400000001741ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a746": "0000000001a76270b8188bbdd60aba522a75eb9643fb12183f9e7a929abc5fb7c0bdfc270200000000ffffffff0280841e000000000000146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54030420074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c046600000009746573746e616d653220d9d0782ee039b29bccf72e07f6a8d0cf4aea50ff9ee1764d762f913f5e14980bbc007a00000000000014f0f269d0cb5eb1e065b1084d1d787a7ec5b6ed8c00000000000002002102df1a2c03a8d1b89b0b2f570ad99764f7dc4bcc710afa9745af65f2bc7789cfc806000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f",
|
||||
"7400000001742bca389fe3871c49fe6ea6706980a247fa4c4e97e60e1ac79598bd151b8055f3": "000000000111da6d1f761ddf9bdb4c9d6e5303ebd41f61858d0a5647a1a7bfe089bf921be900000000ffffffff0180969800000000000014cffd1219803f5f43f72a2a8e841a2fefb0347657000000000000000000000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f",
|
||||
"7400000001749a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d8491": "00000000011b474b762714c2f0adb18d2127d4f32fd8202d64f4f2a031195cb72a98a8f25a01000000ffffffff0280841e000000000000142f6d31b494bdfa42034719ca28894496cdb89359030420aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000009746573746e616d653120fb278d451ca7cf961fece75487723f06d66fbc7326e282b1889a1757066b89055ce37900000000000014222740511246fe2b10957049ae10e0bbe7fa996c000000000000020021023960f57b3d5844794ac0dd5576fb59b3a64d82d4d76285a36173d169412539e205000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f",
|
||||
"740000000174a1f71206f2277bacd0b1fa32a633d20b69deb3e6dc1bc95db78a572d9f309155": "00000000012bca389fe3871c49fe6ea6706980a247fa4c4e97e60e1ac79598bd151b8055f300000000ffffffff0200000000000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa3020320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a040000000009746573746e616d6531d0879800000000000014a62c71694f8bf7583bfe73bd1b2235b81ee12558000000000000020021020c30819abd60558d2f9d1fdfabb5d46df7254b5a9346c9d78dc9b5ace6b1089e0300000001827ba7677abf30338299f8d2ab3c1a0a1144544dac46f5ec486bac54d3a9fe1c660000001054ce1dffffff7f",
|
||||
"740000000174a76270b8188bbdd60aba522a75eb9643fb12183f9e7a929abc5fb7c0bdfc2702": "0000000001e12c22d4f162d9a012c9319233da5d3e923cc5e1029b8f90e47249c9ab256b3500000000ffffffff0180969800000000000014657eb0831fd4fafd3e595163416d76861ea9acd9000000000000000100000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f",
|
||||
"740000000174cc9c76cf691cb577640a511a7e1539e72f5672ea75bf364dd3d2e6add1e10efd": "00000000021ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000ffffffff1ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74601000000ffffffff0280841e000000000000146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54040320074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c046600000020f70095f015fc11f5bae44f6136cbe1bb846103e79e065ea54f9e56f58e5322ab18eb79000000000000145258eb6f21709d00f66fa39deab6e8d5391eae5f00000000000002002103d1ab347d75498ccde90f26907c8aad98fcb5ecc922ff7de8f4be83c9b7a099b6020021021e772405b9b642a904d6e1f5739b56d1714327853f6cbb97777c70ef0c54c5450b000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f",
|
||||
"740000000242aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000": "09746573746e616d6531c0c62d000000000073000000013bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000",
|
||||
"740000000242aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000": "09746573746e616d653180841e000000000073000000009a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000",
|
||||
"740000000245aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000",
|
||||
"740000000245aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000": "1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000",
|
||||
"740000000269aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "09746573746e616d6531c0c62d0000000000ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d46d00000001",
|
||||
"74000000027401f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf": "00000000023bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000ffffffff3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d901000000ffffffff02c0c62d000000000000148e659d46d7ceb9464cc04f3aeb73bc2702c1e7a3040320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a0466000000204c679195282ea315863be7119a2f1f6a4b9486fe7f86e6e778435f4fe9ed4083d8a86a00000000000014bfe57d3d51c357a99aa9c84195fe8e000273399a00000000000002002103aa28c5dc394dd41bda11c296f1e0ef98edfa09e5730b6abdab5f1e1e700e2a61020021036b182787487be70c07012724b985e8fe7f49067f2a3e4c5535da0118a21d13fa0c000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f",
|
||||
"7400000002743bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d9": "0000000001abe815e307b0d125b21242058b6e0a8a43190c86456af1de27c17f0c8ddc29a600000000ffffffff02c0c62d000000000000148e659d46d7ceb9464cc04f3aeb73bc2702c1e7a3030420aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000009746573746e616d653120ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d47cbe6a00000000000014b44aae19323f53e63e5cd184dba19a71616808150000000000000200210237b59c5644219778989265c96ecdb213578feca83fc908a975d8465359779e8807000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f",
|
||||
"740000000274abe815e307b0d125b21242058b6e0a8a43190c86456af1de27c17f0c8ddc29a6": "00000000017b0aa1735e5ba58d3236316c671fe4f00ed366ee72417c9ed02a53a8019e85b800000000ffffffff01809698000000000000148ec33e2b7a9dc5fde7e6a166005d3b29265b313d000000000000000200000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f"
|
||||
},
|
||||
"after": {
|
||||
"5700000000": "077072696d617279",
|
||||
"5700000001": "0777616c6c657431",
|
||||
"5700000002": "0777616c6c657432",
|
||||
"740000000142074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382ccc9c76cf691cb577640a511a7e1539e72f5672ea75bf364dd3d2e6add1e10efd00000000": "09746573746e616d653280841e000000000073000000011ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000",
|
||||
"740000000142aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000": "09746573746e616d6531c0c62d000000000073000000000000000000000000000000000000000000000000000000000000000000000000ffffffff",
|
||||
"740000000142aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000": "09746573746e616d653180841e000000000073000000019a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000",
|
||||
"740000000145074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c1ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000": "cc9c76cf691cb577640a511a7e1539e72f5672ea75bf364dd3d2e6add1e10efd00000000",
|
||||
"740000000145aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000": "1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000",
|
||||
"740000000169074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c1ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000": "09746573746e616d653280841e0000000000d9d0782ee039b29bccf72e07f6a8d0cf4aea50ff9ee1764d762f913f5e14980b6d00000001",
|
||||
"740000000169aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "09746573746e616d6531c0c62d0000000000ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d4ffffffff00",
|
||||
"740000000169aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000": "09746573746e616d653180841e0000000000fb278d451ca7cf961fece75487723f06d66fbc7326e282b1889a1757066b89056d00000001",
|
||||
"7400000001741706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f4": "00000000029a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849100000000ffffffff9a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d849101000000ffffffff0280841e000000000000142f6d31b494bdfa42034719ca28894496cdb89359040320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000020450566fda05dda024354f47c1f6d07769342a57c5dffe1d3a908a9043a83156bb8cd79000000000000149ae567da742bcd8894e5f0c789aa1a5a017396cc000000000000020021037f44c97b1d5c69af830f7c14647c710e95300bd94dd92ad75fdd30ca7489287f020021024e3c15df14b521891f269af40258119698ebaed5f7299abf9583546e2438e73209000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f",
|
||||
"7400000001741b474b762714c2f0adb18d2127d4f32fd8202d64f4f2a031195cb72a98a8f25a": "0000000001a1f71206f2277bacd0b1fa32a633d20b69deb3e6dc1bc95db78a572d9f30915501000000ffffffff02000000000000000000146a94c2244bd9a4b5257a8c946b68af72f4118b35020320074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c040000000009746573746e616d6532207998000000000000141fcdaa5bd5efa8811106f6390e5ecc327083426b0000000000000200210256bc84ba99364f4a0146c1203b52104447d1085aee74df1242be003664e36cdf0400000001827ba7677abf30338299f8d2ab3c1a0a1144544dac46f5ec486bac54d3a9fe1c660000001054ce1dffffff7f",
|
||||
"7400000001741ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a746": "0000000001a76270b8188bbdd60aba522a75eb9643fb12183f9e7a929abc5fb7c0bdfc270200000000ffffffff0280841e000000000000146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54030420074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c046600000009746573746e616d653220d9d0782ee039b29bccf72e07f6a8d0cf4aea50ff9ee1764d762f913f5e14980bbc007a00000000000014f0f269d0cb5eb1e065b1084d1d787a7ec5b6ed8c00000000000002002102df1a2c03a8d1b89b0b2f570ad99764f7dc4bcc710afa9745af65f2bc7789cfc806000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f",
|
||||
"7400000001742bca389fe3871c49fe6ea6706980a247fa4c4e97e60e1ac79598bd151b8055f3": "000000000111da6d1f761ddf9bdb4c9d6e5303ebd41f61858d0a5647a1a7bfe089bf921be900000000ffffffff0180969800000000000014cffd1219803f5f43f72a2a8e841a2fefb0347657000000000000000000000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f",
|
||||
"7400000001749a3ff4271510594109bd09d92271f7ef2fbd70f9e5bcae8a7347f29b7d3d8491": "00000000011b474b762714c2f0adb18d2127d4f32fd8202d64f4f2a031195cb72a98a8f25a01000000ffffffff0280841e000000000000142f6d31b494bdfa42034719ca28894496cdb89359030420aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000009746573746e616d653120fb278d451ca7cf961fece75487723f06d66fbc7326e282b1889a1757066b89055ce37900000000000014222740511246fe2b10957049ae10e0bbe7fa996c000000000000020021023960f57b3d5844794ac0dd5576fb59b3a64d82d4d76285a36173d169412539e205000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f",
|
||||
"740000000174a1f71206f2277bacd0b1fa32a633d20b69deb3e6dc1bc95db78a572d9f309155": "00000000012bca389fe3871c49fe6ea6706980a247fa4c4e97e60e1ac79598bd151b8055f300000000ffffffff0200000000000000000014b9e7138dd8a5fdd11fee499ccb7a6cd131edbaa3020320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a040000000009746573746e616d6531d0879800000000000014a62c71694f8bf7583bfe73bd1b2235b81ee12558000000000000020021020c30819abd60558d2f9d1fdfabb5d46df7254b5a9346c9d78dc9b5ace6b1089e0300000001827ba7677abf30338299f8d2ab3c1a0a1144544dac46f5ec486bac54d3a9fe1c660000001054ce1dffffff7f",
|
||||
"740000000174a76270b8188bbdd60aba522a75eb9643fb12183f9e7a929abc5fb7c0bdfc2702": "0000000001e12c22d4f162d9a012c9319233da5d3e923cc5e1029b8f90e47249c9ab256b3500000000ffffffff0180969800000000000014657eb0831fd4fafd3e595163416d76861ea9acd9000000000000000100000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f",
|
||||
"740000000174cc9c76cf691cb577640a511a7e1539e72f5672ea75bf364dd3d2e6add1e10efd": "00000000021ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74600000000ffffffff1ed9775dad6a119d8f4b54b803790de583a1877f0c1126f8cce41e77c438a74601000000ffffffff0280841e000000000000146337e3ecd9354ba7a5fbe0ce2feb6d3bc5558a54040320074675b3cd135b485a497164408e26b2f077c32aec913c42fde3f80b08b4382c046600000020f70095f015fc11f5bae44f6136cbe1bb846103e79e065ea54f9e56f58e5322ab18eb79000000000000145258eb6f21709d00f66fa39deab6e8d5391eae5f00000000000002002103d1ab347d75498ccde90f26907c8aad98fcb5ecc922ff7de8f4be83c9b7a099b6020021021e772405b9b642a904d6e1f5739b56d1714327853f6cbb97777c70ef0c54c5450b000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f",
|
||||
"740000000242aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000": "09746573746e616d6531c0c62d000000000073000000013bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000",
|
||||
"740000000242aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a1706eab2dce949dd69cd82079f7b4dde902ec7702f0ea1f583c64574fe2341f400000000": "09746573746e616d653180841e000000000073000000000000000000000000000000000000000000000000000000000000000000000000ffffffff",
|
||||
"740000000245aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "01f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf00000000",
|
||||
"740000000269aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000": "09746573746e616d6531c0c62d0000000000ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d46d00000001",
|
||||
"74000000027401f3b3fc483462c0de2f7962542da4e9a62393476d55a47cc29952d69957bbaf": "00000000023bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d900000000ffffffff3bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d901000000ffffffff02c0c62d000000000000148e659d46d7ceb9464cc04f3aeb73bc2702c1e7a3040320aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a0466000000204c679195282ea315863be7119a2f1f6a4b9486fe7f86e6e778435f4fe9ed4083d8a86a00000000000014bfe57d3d51c357a99aa9c84195fe8e000273399a00000000000002002103aa28c5dc394dd41bda11c296f1e0ef98edfa09e5730b6abdab5f1e1e700e2a61020021036b182787487be70c07012724b985e8fe7f49067f2a3e4c5535da0118a21d13fa0c000000017eb84dc58b93e03a71f5d663a2296b8d9dc52950c625b5ae31efd8cea4a0e67b730000008872ce1dffffff7f",
|
||||
"7400000002743bd9079ea3d113b8eb0288c4b2a7c91d08a0cfe45ecad626d56c9251961f28d9": "0000000001abe815e307b0d125b21242058b6e0a8a43190c86456af1de27c17f0c8ddc29a600000000ffffffff02c0c62d000000000000148e659d46d7ceb9464cc04f3aeb73bc2702c1e7a3030420aca5b24203acfd52b9abe29d4adcccad9b8f93a57b495481b4af8a5b0c9f719a046600000009746573746e616d653120ca5a40c98ed472f5726ef80a1e6299b0a713769cb1122aeb80f962cc02a4d8d47cbe6a00000000000014b44aae19323f53e63e5cd184dba19a71616808150000000000000200210237b59c5644219778989265c96ecdb213578feca83fc908a975d8465359779e8807000000015c899b34c8d3bfa15916696ab22daba092a57b6729f7b0d2d80dbd366600783b6d0000007864ce1dffffff7f",
|
||||
"740000000274abe815e307b0d125b21242058b6e0a8a43190c86456af1de27c17f0c8ddc29a6": "00000000017b0aa1735e5ba58d3236316c671fe4f00ed366ee72417c9ed02a53a8019e85b800000000ffffffff01809698000000000000148ec33e2b7a9dc5fde7e6a166005d3b29265b313d000000000000000200000001ed1abdda4d85676c7978e2d3e85c8e2a36316e79b34955cf475a0bd1336bf21765000000b851ce1dffffff7f"
|
||||
}
|
||||
}
|
||||
|
|
@ -70,3 +70,19 @@ function fromU32(num) {
|
|||
data.writeUInt32LE(num, 0, true);
|
||||
return data;
|
||||
}
|
||||
|
||||
walletUtils.dumpWDB = async (wdb, prefixes) => {
|
||||
const data = await wdb.dump();
|
||||
const filtered = {};
|
||||
|
||||
for (const [key, value] of Object.entries(data)) {
|
||||
for (const prefix of prefixes) {
|
||||
if (key.startsWith(prefix)) {
|
||||
filtered[key] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return filtered;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -877,6 +877,7 @@ describe('Wallet HTTP', function() {
|
|||
// the first bids of this test suite
|
||||
assert.equal(bids.length, 3);
|
||||
assert.ok(bids.every(bid => bid.name === name));
|
||||
assert.ok(bids.every(bid => bid.height === nodeCtx.height));
|
||||
|
||||
// tx1
|
||||
assert.ok(bids.find(bid =>
|
||||
|
|
@ -930,6 +931,8 @@ describe('Wallet HTTP', function() {
|
|||
const bids = await wallet.getBidsByName(name);
|
||||
assert.equal(bids.length, 2);
|
||||
|
||||
assert.ok(bids.every(bid => bid.height === nodeCtx.height));
|
||||
|
||||
// there is no value property on bids
|
||||
// from other wallets
|
||||
assert.ok(bids.find(bid =>
|
||||
|
|
@ -949,6 +952,7 @@ describe('Wallet HTTP', function() {
|
|||
assert.equal(bids.length, 1);
|
||||
const [bid] = bids;
|
||||
assert.equal(bid.prevout.hash, tx1.hash);
|
||||
assert.strictEqual(bid.height, nodeCtx.height);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -1010,6 +1014,20 @@ describe('Wallet HTTP', function() {
|
|||
|
||||
const reveals = json.outputs.filter(output => output.covenant.type === types.REVEAL);
|
||||
assert.equal(reveals.length, 3);
|
||||
|
||||
ownedNames.push(name);
|
||||
|
||||
await nodeCtx.mineBlocks(1, cbAddress);
|
||||
|
||||
const allReveals = await wallet.getReveals();
|
||||
assert.strictEqual(allReveals.length, 3);
|
||||
assert.ok(allReveals.every(reveal => reveal.name === name));
|
||||
assert.ok(allReveals.every(reveal => reveal.height === nodeCtx.height));
|
||||
|
||||
const revealsByName = await wallet.getRevealsByName(name);
|
||||
assert.strictEqual(revealsByName.length, 3);
|
||||
assert.ok(revealsByName.every(reveal => reveal.name === name));
|
||||
assert.ok(revealsByName.every(reveal => reveal.height === nodeCtx.height));
|
||||
});
|
||||
|
||||
it('should get all reveals (single player)', async () => {
|
||||
|
|
@ -1059,12 +1077,14 @@ describe('Wallet HTTP', function() {
|
|||
|
||||
{
|
||||
const reveals = await wallet.getReveals();
|
||||
assert.equal(reveals.length, 2);
|
||||
assert.equal(reveals.length, 5);
|
||||
}
|
||||
|
||||
{
|
||||
// a single reveal per name
|
||||
const reveals = await wallet.getRevealsByName(name);
|
||||
const [reveal] = reveals;
|
||||
assert.strictEqual(reveal.height + revealPeriod, nodeCtx.height);
|
||||
assert.equal(reveals.length, 1);
|
||||
}
|
||||
});
|
||||
|
|
@ -1123,24 +1143,52 @@ describe('Wallet HTTP', function() {
|
|||
|
||||
{
|
||||
const reveals = await wallet.getRevealsByName(name, {own: true});
|
||||
assert.equal(reveals.length, 1);
|
||||
assert.strictEqual(reveals.length, 1);
|
||||
const [reveal] = reveals;
|
||||
assert.equal(reveal.own, true);
|
||||
assert.equal(reveal.prevout.hash, r1.hash);
|
||||
assert.strictEqual(reveal.bidPrevout.hash, state.bids[0].hash);
|
||||
assert.strictEqual(reveal.bidPrevout.index, 0);
|
||||
assert.strictEqual(reveal.own, true);
|
||||
assert.strictEqual(reveal.prevout.hash, r1.hash);
|
||||
}
|
||||
|
||||
{
|
||||
const reveals = await wallet.getRevealsByName(name);
|
||||
assert.equal(reveals.length, 2);
|
||||
assert.strictEqual(reveals.length, 2);
|
||||
|
||||
assert.ok(reveals.find(reveal =>
|
||||
reveal.prevout.hash === r1.hash
|
||||
));
|
||||
const r1 = reveals.find(reveal =>
|
||||
reveal.prevout.hash === state.reveals[0].hash);
|
||||
const r2 = reveals.find(reveal =>
|
||||
reveal.prevout.hash === state.reveals[1].hash);
|
||||
|
||||
assert.ok(reveals.find(reveal =>
|
||||
reveal.prevout.hash === r2.hash
|
||||
));
|
||||
assert.ok(r1);
|
||||
assert.ok(r2);
|
||||
|
||||
assert.strictEqual(r1.bidPrevout.hash, state.bids[0].hash);
|
||||
assert.strictEqual(r1.bidPrevout.index, 0);
|
||||
|
||||
assert.strictEqual(r2.bidPrevout.hash, state.bids[1].hash);
|
||||
assert.strictEqual(r2.bidPrevout.index, 0);
|
||||
}
|
||||
|
||||
const dump = await nodeCtx.wdb.dump();
|
||||
const dumpSlice = {};
|
||||
|
||||
Object.keys(dump).filter((key) => {
|
||||
const wid1 = '7400000001';
|
||||
// txdblayout.t
|
||||
if (key.startsWith(wid1 + '74'))
|
||||
dumpSlice[key] = dump[key];
|
||||
|
||||
// txdblayout.i
|
||||
if (key.startsWith(wid1 + '69'))
|
||||
dumpSlice[key] = dump[key];
|
||||
|
||||
// txdblayout.B
|
||||
if (key.startsWith(wid1 + '42'))
|
||||
dumpSlice[key] = dump[key];
|
||||
});
|
||||
|
||||
console.log(dumpSlice);
|
||||
});
|
||||
|
||||
it('should get auction info', async () => {
|
||||
|
|
|
|||
|
|
@ -572,6 +572,7 @@ describe('Wallet Migrations', function() {
|
|||
};
|
||||
|
||||
walletDB.options.walletMigrate = 0;
|
||||
walletDB.version = 2;
|
||||
|
||||
await walletDB.open();
|
||||
const wallet = walletDB.primary;
|
||||
|
|
@ -828,4 +829,91 @@ describe('Wallet Migrations', function() {
|
|||
await walletDB.close();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Bid Reveal Migration (integration)', function() {
|
||||
const location = testdir('wallet-bid-reveal');
|
||||
const migrationsBAK = WalletMigrator.migrations;
|
||||
const data = require('./data/migrations/wallet-4-bid-reveal.json');
|
||||
const Migration = WalletMigrator.MigrateBidRevealEntries;
|
||||
const layout = Migration.layout();
|
||||
|
||||
const walletOptions = {
|
||||
prefix: location,
|
||||
memory: false,
|
||||
network
|
||||
};
|
||||
|
||||
let walletDB, ldb;
|
||||
before(async () => {
|
||||
WalletMigrator.migrations = {};
|
||||
await fs.mkdirp(location);
|
||||
|
||||
walletDB = new WalletDB(walletOptions);
|
||||
ldb = walletDB.db;
|
||||
|
||||
await ldb.open();
|
||||
|
||||
const b = ldb.batch();
|
||||
for (const [key, value] of Object.entries(data.before)) {
|
||||
const bkey = Buffer.from(key, 'hex');
|
||||
const bvalue = Buffer.from(value, 'hex');
|
||||
|
||||
b.put(bkey, bvalue);
|
||||
}
|
||||
await b.write();
|
||||
|
||||
await ldb.close();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
WalletMigrator.migrations = migrationsBAK;
|
||||
await rimraf(location);
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
walletDB = new WalletDB(walletOptions);
|
||||
ldb = walletDB.db;
|
||||
});
|
||||
|
||||
afterEach(async () => {
|
||||
if (ldb.opened)
|
||||
await ldb.close();
|
||||
});
|
||||
|
||||
it('should have before entries', async () => {
|
||||
walletDB.version = 2;
|
||||
await walletDB.open();
|
||||
await checkVersion(ldb, layout.wdb.V.encode(), 2);
|
||||
await checkEntries(ldb, data.before);
|
||||
await walletDB.close();
|
||||
});
|
||||
|
||||
it('should enable wallet migration', () => {
|
||||
WalletMigrator.migrations = {
|
||||
0: Migration
|
||||
};
|
||||
});
|
||||
|
||||
it('should fail without migrate flag', async () => {
|
||||
const expectedError = migrationError(WalletMigrator.migrations, [0],
|
||||
wdbFlagError(0));
|
||||
|
||||
await assert.rejects(async () => {
|
||||
await walletDB.open();
|
||||
}, {
|
||||
message: expectedError
|
||||
});
|
||||
|
||||
await ldb.close();
|
||||
});
|
||||
|
||||
it('should migrate', async () => {
|
||||
walletDB.options.walletMigrate = 0;
|
||||
|
||||
await walletDB.open();
|
||||
// check we have migrated entries.
|
||||
await checkEntries(ldb, data.after);
|
||||
await walletDB.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -2177,6 +2177,7 @@ describe('Wallet', function() {
|
|||
|
||||
// Hack required to focus test on txdb mechanics.
|
||||
// We don't otherwise need WalletDB or Blockchain
|
||||
// TODO: Remove this after #888 is merged.
|
||||
wdb.getRenewalBlock = () => {
|
||||
return network.genesis.hash;
|
||||
};
|
||||
|
|
@ -2341,6 +2342,7 @@ describe('Wallet', function() {
|
|||
);
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addInput(dummyInput());
|
||||
mtx.outputs.push(output);
|
||||
|
||||
// Confirm external REVEAL
|
||||
|
|
@ -2546,6 +2548,7 @@ describe('Wallet', function() {
|
|||
|
||||
// Hack required to focus test on txdb mechanics.
|
||||
// We don't otherwise need WalletDB or Blockchain
|
||||
// TODO: Remove this after #888 is merged.
|
||||
wdb.getRenewalBlock = () => {
|
||||
return network.genesis.hash;
|
||||
};
|
||||
|
|
@ -2707,6 +2710,7 @@ describe('Wallet', function() {
|
|||
);
|
||||
|
||||
const mtx = new MTX();
|
||||
mtx.addInput(dummyInput());
|
||||
mtx.outputs.push(output);
|
||||
|
||||
// Confirm external REVEAL
|
||||
|
|
@ -3180,6 +3184,7 @@ describe('Wallet', function() {
|
|||
|
||||
// Hack required to focus test on txdb mechanics.
|
||||
// We don't otherwise need WalletDB or Blockchain
|
||||
// TODO: Remove this after #888 is merged.
|
||||
wdb.getRenewalBlock = () => {
|
||||
return network.genesis.hash;
|
||||
};
|
||||
|
|
@ -3626,4 +3631,135 @@ describe('Wallet', function() {
|
|||
assert.strictEqual(await bob.txdb.hasBlind(expectedBlinds.bob), true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Bid and Reveal by Reveal and Bid', function () {
|
||||
const network = Network.get('regtest');
|
||||
const wdb = new WalletDB({ network });
|
||||
|
||||
// Hack required to focus test on txdb mechanics.
|
||||
// We don't otherwise need WalletDB or Blockchain
|
||||
// TODO: Remove this after #888 is merged.
|
||||
wdb.getRenewalBlock = () => {
|
||||
return network.genesis.hash;
|
||||
};
|
||||
|
||||
const mineBlocks = async (count) => {
|
||||
for (let i = 0; i < count; i++) {
|
||||
await wdb.addBlock(nextEntry(wdb), []);
|
||||
}
|
||||
};
|
||||
|
||||
const NAME = rules.grindName(10, 1, network);
|
||||
const NAMEHASH = rules.hashString(NAME);
|
||||
let wallet;
|
||||
|
||||
const BASE_BID = 1e6;
|
||||
const BASE_LOCKUP = 2e6;
|
||||
const BID_COUNT = 5;
|
||||
const bids = [];
|
||||
let revealMTX;
|
||||
|
||||
before(async () => {
|
||||
await wdb.open();
|
||||
await wdb.connect();
|
||||
|
||||
wallet = await wdb.create();
|
||||
});
|
||||
|
||||
after(async () => {
|
||||
await wdb.disconnect();
|
||||
await wdb.close();
|
||||
});
|
||||
|
||||
it('should fund wallet', async () => {
|
||||
const addr = await wallet.receiveAddress();
|
||||
|
||||
const txs = [];
|
||||
for (let i = 0; i < BID_COUNT; i++) {
|
||||
const mtx = new MTX();
|
||||
mtx.addOutpoint(new Outpoint(Buffer.alloc(32), 0));
|
||||
mtx.addOutput(addr, 20e6);
|
||||
txs.push(mtx.toTX());
|
||||
}
|
||||
|
||||
await wdb.addBlock(nextEntry(wdb), txs);
|
||||
});
|
||||
|
||||
it('should open names', async () => {
|
||||
const open = await wallet.createOpen(NAME);
|
||||
await wdb.addBlock(nextEntry(wdb), [open.toTX()]);
|
||||
await mineBlocks(network.names.treeInterval + 1);
|
||||
});
|
||||
|
||||
it('should create and get bid', async () => {
|
||||
for (let i = 0; i < BID_COUNT; i++) {
|
||||
const bid = await wallet.createBid(NAME, BASE_BID + i, BASE_LOCKUP + i);
|
||||
await wdb.addTX(bid.toTX());
|
||||
bids.push(bid);
|
||||
}
|
||||
|
||||
const txs = bids.map(b => b.toTX());
|
||||
await wdb.addBlock(nextEntry(wdb), txs);
|
||||
|
||||
for (const [index, bid] of bids.entries()) {
|
||||
const bidOut = bid.outpoint(0);
|
||||
const blindBid = await wallet.getBid(NAMEHASH, bidOut);
|
||||
assert.ok(blindBid);
|
||||
assert.bufferEqual(blindBid.nameHash, NAMEHASH);
|
||||
assert.bufferEqual(blindBid.prevout.hash, bid.hash());
|
||||
assert.strictEqual(blindBid.prevout.index, 0);
|
||||
assert.strictEqual(blindBid.lockup, BASE_LOCKUP + index);
|
||||
assert.strictEqual(blindBid.height, wdb.state.height);
|
||||
assert.strictEqual(blindBid.own, true);
|
||||
}
|
||||
|
||||
await mineBlocks(network.names.biddingPeriod);
|
||||
});
|
||||
|
||||
it('should create and get reveal', async () => {
|
||||
revealMTX = await wallet.createReveal(NAME);
|
||||
await wdb.addBlock(nextEntry(wdb), [revealMTX.toTX()]);
|
||||
|
||||
for (const [index, out] of revealMTX.outputs.entries()) {
|
||||
if (!out.covenant.isReveal())
|
||||
continue;
|
||||
|
||||
const revealOut = revealMTX.outpoint(index);
|
||||
const bidReveal = await wallet.getReveal(NAMEHASH, revealOut);
|
||||
|
||||
assert.ok(bidReveal);
|
||||
assert.bufferEqual(bidReveal.prevout.hash, revealMTX.hash());
|
||||
assert.strictEqual(bidReveal.prevout.index, index);
|
||||
assert.bufferEqual(bidReveal.nameHash, NAMEHASH);
|
||||
assert.strictEqual(bidReveal.height, wdb.state.height);
|
||||
assert.strictEqual(bidReveal.own, true);
|
||||
}
|
||||
});
|
||||
|
||||
it('should get reveal by bid', async () => {
|
||||
for (const [index, bidTX] of bids.entries()) {
|
||||
const bidOut = bidTX.outpoint(0);
|
||||
const bidReveal = await wallet.getRevealByBid(NAMEHASH, bidOut);
|
||||
assert.ok(bidReveal);
|
||||
|
||||
assert.bufferEqual(bidReveal.bidPrevout.hash, bidOut.hash);
|
||||
assert.strictEqual(bidReveal.bidPrevout.index, bidOut.index);
|
||||
assert.strictEqual(bidReveal.value, BASE_BID + index);
|
||||
}
|
||||
});
|
||||
|
||||
it('should get bid by reveal', async () => {
|
||||
for (const bidTX of bids) {
|
||||
const bidOut = bidTX.outpoint(0);
|
||||
const bidReveal = await wallet.getRevealByBid(NAMEHASH, bidOut);
|
||||
assert.ok(bidReveal);
|
||||
|
||||
const origBlindBid = await wallet.getBid(NAMEHASH, bidOut);
|
||||
const blindBid = await wallet.getBidByReveal(NAMEHASH,
|
||||
new Outpoint(bidReveal.prevout.hash, bidReveal.prevout.index));
|
||||
|
||||
assert.deepStrictEqual(blindBid, origBlindBid);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue