handshake: wallet work.

This commit is contained in:
Christopher Jeffrey 2018-01-09 14:30:37 -08:00
parent 5c581b4eb3
commit 2ef47124fc
No known key found for this signature in database
GPG key ID: 8962AB9DE6666BBD
13 changed files with 545 additions and 154 deletions

View file

@ -108,8 +108,9 @@ class CoinEntry {
this.version = coin.version;
this.height = coin.height;
this.coinbase = coin.coinbase;
this.output.address = coin.address;
this.output.value = coin.value;
this.output.address = coin.address;
this.output.covenant = coin.covenant;
return this;
}

View file

@ -171,6 +171,15 @@ class Auction {
auction.bids = br.readU32();
return auction;
}
toJSON() {
return {
name: this.name.toString('ascii'),
owner: this.owner.toJSON(),
height: this.height,
renewal: this.renewal
};
}
}
Auction.states = states;

View file

@ -140,6 +140,7 @@ class NameDB {
}
async prove(root, key) {
return this.trie.prove(key);
const trie = this.trie.snapshot(root);
return trie.prove(key);
}
@ -210,6 +211,11 @@ class NameDB {
auction.ops.length = 0;
}
async getDataByName(name, height) {
const key = blake2b.digest(name);
return this.getData(key, height);
}
async getData(key, height) {
const auction = await this.getAuction(key);
@ -275,10 +281,11 @@ class NameDB {
return false;
// XXX util.revHex
// Must commit to last 4 bytes of the block hash.
// Must commit to last 8 bytes of the block hash.
// Presigning a renewal would require 1.1151 exabytes of storage.
const tail = covenant.items[4].toString('hex');
if (entry.slice(0, 8) !== tail)
if (entry.slice(0, 16) !== tail)
return false;
return true;

View file

@ -164,8 +164,8 @@ exports.hasSaneCovenants = function hasSaneCovenants(tx) {
if (i >= tx.inputs.length)
return false;
// Should contain a nonce and height.
if (covenant.items.length !== 3)
// Should contain a nonce.
if (covenant.items.length !== 2)
return false;
// Name must be valid.
@ -176,10 +176,6 @@ exports.hasSaneCovenants = function hasSaneCovenants(tx) {
if (covenant.items[1].length !== 32)
return false;
// Height the user bid at.
if (covenant.items[2].length !== 4)
return false;
break;
}
case types.REGISTER: {
@ -362,10 +358,6 @@ exports.verifyCovenants = function verifyCovenants(tx, view, height) {
if (!blind.equals(uc.items[1]))
return false;
// Height must match their bid height.
if (covenant.items[2].readUInt32LE(0, true) !== entry.height)
return false;
// If they lied to us, they can
// never redeem their money.
if (coin.value < output.value)

View file

@ -80,7 +80,7 @@ class HTTP extends Server {
*/
initRouter() {
this.use(this.cors());
// this.use(this.cors());
if (!this.options.noAuth) {
this.use(this.basicAuth({

View file

@ -33,6 +33,8 @@ const Output = require('../primitives/output');
const TX = require('../primitives/tx');
const consensus = require('../protocol/consensus');
const pkg = require('../pkg');
const rules = require('../covenants/rules');
const {TrieProof} = require('thc/trie/proof');
const {EXP} = consensus;
const RPCBase = bweb.RPC;
const RPCError = bweb.RPCError;
@ -228,6 +230,8 @@ class RPC extends RPCBase {
this.add('getmemoryinfo', this.getMemoryInfo);
this.add('setloglevel', this.setLogLevel);
this.add('getnamestatus', this.getNameStatus);
this.add('getnameproof', this.getNameProof);
}
/*
@ -2223,6 +2227,55 @@ class RPC extends RPCBase {
return null;
}
async getNameStatus(args, help) {
if (help || args.length !== 1)
throw new RPCError(errs.MISC_ERROR, 'getnamestatus "name"');
const valid = new Validator(args);
const name = valid.str(0);
if (!name)
throw new RPCError(errs.TYPE_ERROR, 'Invalid name.');
const raw = Buffer.from(name, 'ascii');
const week = blake2b.digest(raw)[0] % 52;
const start = week * rules.ROLLOUT_INTERVAL;
const auction = await this.chain.cdb.getAuctionByName(raw);
const data = await this.chain.cdb.getDataByName(raw);
return {
start,
auction: auction ? auction.toJSON() : null,
data: data ? data.toString('hex') : null
};
}
async getNameProof(args, help) {
if (help || args.length !== 1)
throw new RPCError(errs.MISC_ERROR, 'getnameproof "name"');
const valid = new Validator(args);
const name = valid.str(0);
if (!name)
throw new RPCError(errs.TYPE_ERROR, 'Invalid name.');
const raw = Buffer.from(name, 'ascii');
const key = blake2b.digest(raw);
const height = this.chain.tip.height;
const root = this.chain.tip.trieRoot;
const nodes = await this.chain.cdb.prove(root, key);
const data = await this.chain.cdb.getData(key);
const proof = new TrieProof(root, key, nodes);
proof.height = height;
if (data)
proof.data = data;
return proof.toJSON();
}
/*
* Helpers
*/

View file

@ -78,6 +78,27 @@ class Covenant {
return this.type > rules.MAX_COVENANT_TYPE;
}
getString(index, enc) {
assert(index < this.items.length);
return this.items[index].toString(enc || 'ascii');
}
getU32(index) {
assert(index < this.items.length);
assert(this.items[index].length === 4);
return this.items[index].readUInt32LE(0, true);
}
getU64(index) {
assert(index < this.items.length);
assert(this.items[index].length === 8);
try {
return encoding.readU64(this.items[index], 0);
} catch (e) {
return -1;
}
}
/**
* Convert witness to an array of buffers.
* @returns {Buffer[]}

View file

@ -77,7 +77,7 @@ class HTTP extends Server {
*/
initRouter() {
this.use(this.cors());
// this.use(this.cors());
if (!this.options.noAuth) {
this.use(this.basicAuth({

View file

@ -93,7 +93,9 @@ exports.txdb = {
// Auction records
A: bdb.key('A', ['ascii']),
// Bids
B: bdb.key('B', ['ascii']),
// Values
v: bdb.key('v', ['ascii'])
i: bdb.key('i', ['ascii', 'hash256', 'uint32']),
// Reveals
B: bdb.key('B', ['ascii', 'hash256', 'uint32']),
// Blinds
v: bdb.key('v', ['hash256'])
};

View file

@ -162,6 +162,9 @@ class RPC extends RPCBase {
this.add('selectwallet', this.selectWallet);
this.add('getmemoryinfo', this.getMemoryInfo);
this.add('setloglevel', this.setLogLevel);
this.add('getbids', this.getBids);
this.add('getauctions', this.getAuctions);
this.add('getauctioninfo', this.getAuctionInfo);
this.add('sendbid', this.sendBid);
this.add('sendreveal', this.sendReveal);
this.add('sendregister', this.sendRegister);
@ -1618,6 +1621,102 @@ class RPC extends RPCBase {
return null;
}
async getBids(args, help) {
if (help || args.length > 1)
throw new RPCError(errs.MISC_ERROR, 'getbids "name"');
const wallet = this.wallet;
const valid = new Validator(args);
const name = valid.str(0);
const bids = await wallet.getBids(name);
const items = [];
for (const bid of bids) {
items.push({
name: !name ? bid.name : undefined,
prevout: bid.prevout.toJSON(),
blind: bid.blind.toString('hex'),
lockup: bid.lockup,
value: bid.value,
nonce: bid.nonce ? bid.nonce.toString('hex') : null
});
}
return items;
}
async getAuctions(args, help) {
if (help || args.length !== 0)
throw new RPCError(errs.MISC_ERROR, 'getauctions');
const wallet = this.wallet;
const valid = new Validator(args);
const name = valid.str(0);
const auctions = await wallet.getAuctions();
const items = [];
for (const auction of auctions) {
items.push({
name: auction.name,
owner: auction.owner.toJSON(),
state: auction.state,
height: auction.height
});
}
return items;
}
async getAuctionInfo(args, help) {
if (help || args.length !== 1)
throw new RPCError(errs.MISC_ERROR, 'getauctioninfo "name"');
const wallet = this.wallet;
const valid = new Validator(args);
const name = valid.str(0);
const auction = await wallet.getAuction(name);
if (!auction)
throw new RPCError(errs.MISC_ERROR, 'Auction not found.');
const bids = await wallet.getBids(name);
const reveals = await wallet.getReveals(name);
const items = [];
const info = {
name,
owner: auction.owner.toJSON(),
state: auction.state,
height: auction.height,
bids: [],
reveals: []
};
for (const bid of bids) {
info.bids.push({
prevout: bid.prevout.toJSON(),
blind: bid.blind.toString('hex'),
lockup: bid.lockup,
value: bid.value,
nonce: bid.nonce ? bid.nonce.toString('hex') : null
});
}
for (const reveal of reveals) {
info.reveals.push({
prevout: reveal.prevout.toJSON(),
value: reveal.value,
height: reveal.height,
own: reveal.own
});
}
return info;
}
async sendBid(args, help) {
if (help || args.length !== 3)
throw new RPCError(errs.MISC_ERROR, 'sendbid "name" bid value');
@ -1660,7 +1759,7 @@ class RPC extends RPCBase {
async sendRegister(args, help) {
if (help || args.length !== 2)
throw new RPCError(errs.MISC_ERROR, 'sendregister "name"');
throw new RPCError(errs.MISC_ERROR, 'sendregister "name" "data"');
const wallet = this.wallet;
const valid = new Validator(args);

View file

@ -553,7 +553,7 @@ class TXDB {
const output = tx.outputs[i];
const path = await this.getPath(output);
await this.connectCovenant(tx, i, path, height);
await this.insertCovenant(b, state, tx, i, path, height);
if (!path)
continue;
@ -648,29 +648,137 @@ class TXDB {
if (height === 0xffffffff)
height = -1;
return {
owner,
state,
height
};
return { owner, state, height };
}
async getAuctions() {
const iter = this.bucket.iterator({
gte: layout.A.min(),
lte: layout.A.max(),
values: true
});
const auctions = [];
await iter.each((key, raw) => {
const name = layout.A.parse(key);
const br = bio.read(raw);
const owner = Outpoint.fromReader(br);
const state = br.readU8();
let height = br.readU32();
if (height === 0xffffffff)
height = -1;
auctions.push({
name,
owner,
state,
height
});
});
return auctions;
}
putAuction(b, name, owner, state, height) {
if (!owner)
owner = new Outpoint();
const bw = bio.write(36 + 1 + 4);
owner.toWriter(bw);
bw.writeU8(state);
if (height === -1)
height = 0xffffffff;
bw.writeU8(state);
bw.writeU32(height);
b.put(layout.A.build(name), bw.render());
}
removeAuction(b, name) {
b.del(layout.B.build(name));
b.del(layout.A.build(name));
}
async hasBid(name, {hash, index}) {
return this.bucket.has(layout.i.build(name, hash, index));
}
async getBid(name, {hash, index}) {
const raw = await this.bucket.get(layout.i.build(name, hash, index));
if (!raw)
return null;
const br = bio.read(raw);
const blind = br.readBytes(32);
const value = br.readU64();
return {
name,
prevout: new Outpoint(hash, index),
blind,
value
};
}
putBid(b, name, {hash, index}, blind, value) {
const bw = bio.write(40);
bw.writeBytes(blind);
bw.writeU64(value);
b.put(layout.i.build(name, hash, index), bw.render());
}
async getBids(name) {
const iter = this.bucket.iterator({
gte: name ? layout.i.min(name) : layout.i.min(),
lte: name ? layout.i.max(name) : layout.i.max(),
values: true
});
const bids = [];
await iter.each(async (key, raw) => {
const [name, hash, index] = layout.i.parse(key);
const prevout = new Outpoint(hash, index);
const br = bio.read(raw);
const blind = br.readBytes(32);
const lockup = br.readU64();
let value = -1;
let nonce = null;
const b = await this.getBlind(blind);
if (b) {
value = b.value;
nonce = b.nonce;
}
bids.push({
name,
prevout,
blind,
lockup,
value,
nonce
});
});
return bids;
}
async removeBids(b, name) {
const iter = this.bucket.iterator({
gte: layout.i.min(name),
lte: layout.i.max(name)
});
await iter.each(k => b.del(k));
}
async hasReveal(name, {hash, index}) {
@ -710,6 +818,46 @@ class TXDB {
b.put(layout.B.build(name, hash, index), bw.render());
}
async saveReveal(name, outpoint, value, height) {
const b = this.bucket.batch();
this.putReveal(b, name, outpoint, value, height);
await b.write();
}
async getReveals(name) {
const iter = this.bucket.iterator({
gte: name ? layout.B.min(name) : layout.B.min(),
lte: name ? layout.B.max(name) : layout.B.max(),
values: true
});
const reveals = [];
await iter.each(async (key, raw) => {
const [name, hash, index] = layout.B.parse(key);
const prevout = new Outpoint(hash, index);
const br = bio.read(raw);
const value = br.readU64();
let height = br.readU32();
if (height === 0xffffffff)
height = -1;
const own = await this.hasCoin(hash, index);
reveals.push({
name,
prevout,
value,
height,
own
});
});
return reveals;
}
async removeReveals(b, name) {
const iter = this.bucket.iterator({
gte: layout.B.min(name),
@ -719,42 +867,7 @@ class TXDB {
await iter.each(k => b.del(k));
}
async hasValue(name) {
return this.bucket.has(layout.v.build(name));
}
async getValue(name) {
const raw = await this.bucket.get(layout.v.build(name));
if (!raw)
return null;
const br = bio.read(raw);
const value = br.readU64();
const nonce = br.readBytes(32);
return {
value,
nonce
};
}
putValue(b, name, value, nonce) {
const bw = bio.write(8 + 32);
bw.writeU64(value);
bw.writeBytes(nonce);
b.put(layout.v.build(name), bw.render());
}
async saveValue(name, value, nonce) {
const b = this.bucket.batch();
this.putValue(b, name, value, nonce);
await b.write();
}
async pickWinner(name) {
async getWinningReveal(name) {
const iter = this.bucket.iterator({
gte: layout.B.min(name),
lte: layout.B.max(name),
@ -765,7 +878,7 @@ class TXDB {
let winner = null;
await iter.each((key, data) => {
const value = br.read(data).readU64();
const value = bio.read(data).readU64();
if (value >= best) {
const [, hash, index] = layout.B.parse(key);
@ -780,16 +893,51 @@ class TXDB {
return winner;
}
async isWinner(name, owner) {
const winner = await this.pickWinner(name);
async isWinningReveal(name, owner) {
const winner = await this.getWinningReveal(name);
return winner.equals(owner);
}
async connectCovenant(b, tx, i, path, height) {
async hasBlind(blind) {
return this.bucket.has(layout.v.build(blind));
}
async getBlind(blind) {
const raw = await this.bucket.get(layout.v.build(blind));
if (!raw)
return null;
const br = bio.read(raw);
const value = br.readU64();
const nonce = br.readBytes(32);
return {
value,
nonce
};
}
putBlind(b, blind, value, nonce) {
const bw = bio.write(8 + 32);
bw.writeU64(value);
bw.writeBytes(nonce);
b.put(layout.v.build(blind), bw.render());
}
async saveBlind(blind, value, nonce) {
const b = this.bucket.batch();
this.putBlind(b, blind, value, nonce);
await b.write();
}
async insertCovenant(b, state, tx, i, path, height) {
const output = tx.outputs[i];
const {covenant} = output;
if (covenant.type === rules.NONE)
if (covenant.type === rules.types.NONE)
return;
if (covenant.type > rules.MAX_COVENANT_TYPE)
@ -800,51 +948,80 @@ class TXDB {
const name = output.covenant.items[0];
switch (covenant.type) {
case rules.BID: {
case rules.types.BID: {
if (!path)
break;
this.putAuction(b, name, outpoint, rules.BID, height);
state.locked(path, output.value);
break;
}
case rules.REVEAL: {
if (!await this.hasAuction(name))
break;
this.putReveal(b, name, outpoint, output.value, height);
if (path) {
const nonce = output.covenant.items[1];
const height = output.covenant.items[2].readUInt32LE(0, true);
const {hash, index} = tx.inputs[i].prevout;
const {coin} = await this.getCredit(hash, index);
state.locked(path, -coin.value);
state.locked(path, output.value);
this.putValue(b, name, output.value, nonce);
this.putAuction(b, name, outpoint, rules.REVEAL, height);
if (!await this.hasAuction(name)) {
this.putAuction(b, name, null, rules.types.BID, height);
await this.addNameMap(b, name);
}
const blind = covenant.items[1];
this.putBid(b, name, outpoint, blind, output.value);
state.locked(path, output.value, height);
break;
}
case rules.REDEEM: {
if (!await this.hasAuction(name))
case rules.types.REVEAL: {
const auction = await this.getAuction(name);
if (!auction)
break;
if (!path)
if (!path) {
const b = this.bucket.batch();
this.putReveal(b,
name,
outpoint,
output.value,
height
);
this.putAuction(b, name, null, rules.types.REVEAL, auction.height);
await b.write();
break;
}
const {prevout} = tx.inputs[i];
const {hash, index} = prevout;
const coin = await this.getCoin(hash, index);
assert(coin);
state.locked(path, -coin.value + output.value, height);
const nonce = output.covenant.items[1];
const blind = coin.covenant.items[1];
this.putBid(b, name, prevout, blind, coin.value);
this.putBlind(b, blind, output.value, nonce);
this.putReveal(b, name, outpoint, output.value, height);
this.putAuction(b, name, null, rules.types.REVEAL, auction.height);
break;
}
case rules.types.REDEEM: {
const auction = await this.getAuction(name);
if (!auction)
break;
state.locked(path, -output.value);
if (!path) {
const b = this.bucket.batch();
this.putAuction(b, name, null, rules.types.REGISTER, auction.height);
await b.write();
break;
}
state.locked(path, -output.value, height);
this.removeAuction(b, name);
await this.removeBids(b, name);
await this.removeReveals(b, name);
break;
}
case rules.REGISTER: {
if (!await this.hasAuction(name))
break;
case rules.types.REGISTER: {
if (!path) {
// Somebody else registered.
this.removeAuction(b, name);
@ -852,30 +1029,25 @@ class TXDB {
break;
}
this.putAuction(b, name, outpoint, rules.REGISTER, -1);
this.putAuction(b, name, outpoint, rules.types.REGISTER, -1);
await this.removeBids(b, name);
await this.removeReveals(b, name);
break;
}
case rules.TRANSFER: {
if (!await this.hasAuction(name))
break;
case rules.types.TRANSFER: {
if (!path)
break;
this.putAuction(b, name, outpoint, rules.TRANSFER, -1);
this.putAuction(b, name, outpoint, rules.types.TRANSFER, -1);
break;
}
case rules.REVOKE: {
if (!await this.hasAuction(name))
break;
case rules.types.REVOKE: {
if (!path)
break;
this.putAuction(b, name, outpoint, rules.REVOKE, -1);
this.putAuction(b, name, outpoint, rules.types.REVOKE, -1);
break;
}
@ -2346,7 +2518,8 @@ class Balance {
this.coin = 0;
this.unconfirmed = 0;
this.confirmed = 0;
this.locked = 0;
this.ulocked = 0;
this.clocked = 0;
}
/**
@ -2359,12 +2532,15 @@ class Balance {
balance.coin += this.coin;
balance.unconfirmed += this.unconfirmed;
balance.confirmed += this.confirmed;
balance.ulocked += this.ulocked;
balance.clocked += this.clocked;
assert(balance.tx >= 0);
assert(balance.coin >= 0);
assert(balance.unconfirmed >= 0);
assert(balance.confirmed >= 0);
assert(balance.locked >= 0);
assert(balance.ulocked >= 0);
assert(balance.clocked >= 0);
}
/**
@ -2373,13 +2549,14 @@ class Balance {
*/
toRaw() {
const bw = bio.write(40);
const bw = bio.write(48);
bw.writeU64(this.tx);
bw.writeU64(this.coin);
bw.writeU64(this.unconfirmed);
bw.writeU64(this.confirmed);
bw.writeU64(this.locked);
bw.writeU64(this.ulocked);
bw.writeU64(this.clocked);
return bw.render();
}
@ -2397,7 +2574,8 @@ class Balance {
this.coin = br.readU64();
this.unconfirmed = br.readU64();
this.confirmed = br.readU64();
this.locked = br.readU64();
this.ulocked = br.readU64();
this.clocked = br.readU64();
return this;
}
@ -2425,7 +2603,8 @@ class Balance {
coin: this.coin,
unconfirmed: this.unconfirmed,
confirmed: this.confirmed,
locked: this.locked
lockedUnconfirmed: this.ulocked,
lockedConfirmed: this.clocked
};
}
@ -2440,7 +2619,8 @@ class Balance {
+ ` coin=${this.coin}`
+ ` unconfirmed=${Amount.coin(this.unconfirmed)}`
+ ` confirmed=${Amount.coin(this.confirmed)}`
+ ` locked=${Amount.coin(this.locked)}`
+ ` lockedUnconfirmed=${Amount.coin(this.ulocked)}`
+ ` lockedConfirmed=${Amount.coin(this.clocked)}`
+ '>';
}
}
@ -2500,10 +2680,23 @@ class BalanceDelta {
this.wallet.confirmed += value;
}
locked(path, value) {
ulocked(path, value) {
const account = this.get(path);
account.locked += value;
this.wallet.locked += value;
account.ulocked += value;
this.wallet.ulocked += value;
}
clocked(path, value) {
const account = this.get(path);
account.clocked += value;
this.wallet.clocked += value;
}
locked(path, value, height) {
if (height === -1)
this.ulocked(path, value);
else
this.clocked(path, value);
}
}

View file

@ -15,6 +15,7 @@ const bio = require('bufio');
const hash160 = require('bcrypto/lib/hash160');
const hash256 = require('bcrypto/lib/hash256');
const cleanse = require('bcrypto/lib/cleanse');
const random = require('bcrypto/lib/random');
const TXDB = require('./txdb');
const Path = require('./path');
const common = require('./common');
@ -1128,14 +1129,31 @@ class Wallet extends EventEmitter {
assert(mtx.getFee() <= MTX.Selector.MAX_FEE, 'TX exceeds MAX_FEE.');
}
async createBid(name, bid, value, options) {
if (!options)
options = {};
async getAuctions() {
return this.txdb.getAuctions();
}
const auction = await this.txdb.getAuction(name);
async getAuction(name) {
return this.txdb.getAuction(name);
}
async getBlind(blind) {
return this.txdb.getBlind(blind);
}
async getBids(name) {
return this.txdb.getBids(name);
}
async getReveals(name) {
return this.txdb.getReveals(name);
}
async createBid(name, bid, value, options) {
const auction = await this.getAuction(name);
if (auction && auction.state !== rules.types.BID)
throw new Error('Auction not found.');
throw new Error('Bidding has closed.');
if (bid > value)
throw new Error('Bid exceeds lockup value.');
@ -1154,9 +1172,7 @@ class Wallet extends EventEmitter {
output.covenant.items.push(raw);
output.covenant.items.push(blind);
// XXX POssibly index by value hash.
// Maybe don't need to store state on auction? Use UTXO?
await this.txdb.saveValue(name, bid, nonce);
await this.txdb.saveBlind(blind, bid, nonce);
const mtx = new MTX();
mtx.outputs.push(output);
@ -1174,29 +1190,34 @@ class Wallet extends EventEmitter {
}
async createReveal(name, options) {
const auction = await this.txdb.getAuction(name);
const auction = await this.getAuction(name);
if (!auction)
throw new Error('Auction not found.');
const reveal = await this.txdb.getValue(name);
if (!reveal)
throw new Error('Reveal value not found.');
const raw = Buffer.from(name, 'ascii');
const {owner, state} = auction;
const {value, nonce} = reveal;
if (state !== rules.types.BID)
throw new Error('Already revealed.');
const {hash, index} = owner;
const coin = await this.getCoin(hash, index)
const bids = await this.getBids();
if (bids.length === 0)
throw new Error('No bids found.');
const {prevout} = bids[0];
const {hash, index} = prevout;
const coin = await this.getCoin(hash, index);
assert(coin);
const height = Buffer.allocUnsafe(4);
height.writeUInt32LE(coin.height, 0, true);
const blind = coin.covenant.items[1];
const reveal = await this.getBlind(blind);
if (!reveal)
throw new Error('Reveal value not found.');
const {value, nonce} = reveal;
const output = new Output();
output.address = coin.address;
@ -1204,7 +1225,6 @@ class Wallet extends EventEmitter {
output.covenant.type = rules.types.REVEAL;
output.covenant.items.push(raw);
output.covenant.items.push(nonce);
output.covenant.items.push(height);
const mtx = new MTX();
mtx.addOutpoint(prevout);
@ -1223,19 +1243,14 @@ class Wallet extends EventEmitter {
}
async createRegister(name, data, options, renew = false) {
const auction = await this.txdb.getAuction(name);
const auction = await this.getAuction(name);
if (!auction)
throw new Error('Auction not found.');
const reveal = await this.txdb.getValue(name);
if (!reveal)
throw new Error('Reveal value not found.');
const raw = Buffer.from(name, 'ascii');
const {owner, state} = auction;
const {value} = reveal;
let {owner, state} = auction;
if (state !== rules.types.REVEAL
&& state !== rules.types.REGISTER
@ -1244,8 +1259,11 @@ class Wallet extends EventEmitter {
}
if (state === rules.types.REVEAL) {
if (!await this.txdb.isWinner(name, owner))
const winner = await this.txdb.getWinningReveal(name);
const {hash, index} = winner;
if (!await this.txdb.hasCoin(hash, index))
throw new Error('Wallet did not win the auction.');
owner = winner;
}
const output = new Output();
@ -1254,7 +1272,7 @@ class Wallet extends EventEmitter {
assert(coin);
output.address = coin.address;
output.value = value;
output.value = coin.value;
output.covenant.type = rules.types.REGISTER;
output.covenant.items.push(raw);
output.covenant.items.push(data);
@ -1283,7 +1301,7 @@ class Wallet extends EventEmitter {
}
const mtx = new MTX();
mtx.addOutpoint(prevout);
mtx.addOutpoint(owner);
mtx.outputs.push(output);
await this._fund(mtx, options);
@ -1437,7 +1455,7 @@ class Wallet extends EventEmitter {
// Consensus sanity checks.
assert(mtx.isSane(), 'TX failed sanity check.');
assert(mtx.verifyInputs(this.wdb.state.height + 1),
assert(mtx.verifyInputs(this.wdb.height + 1),
'TX failed context check.');
const total = await this.template(mtx);

View file

@ -552,16 +552,12 @@ class MemWallet {
const coin = this.getCoin(prevout.toKey())
assert(coin);
const height = Buffer.allocUnsafe(4);
height.writeUInt32LE(coin.height, 0, true);
const output = new Output();
output.address = coin.address;
output.value = value;
output.covenant.type = 2;
output.covenant.items.push(raw);
output.covenant.items.push(nonce);
output.covenant.items.push(height);
const mtx = new MTX();
mtx.addOutpoint(prevout);