Skip to content

Commit

Permalink
Merge PR #829 from '5.x-proposal'
Browse files Browse the repository at this point in the history
  • Loading branch information
nodech committed Jul 18, 2023
2 parents 66d9e3c + 043076a commit 44fab68
Show file tree
Hide file tree
Showing 16 changed files with 185 additions and 19 deletions.
9 changes: 9 additions & 0 deletions docs/release-notes/release-notes-5.x.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ v5.x Release notes
+ [Node changes](#node-changes)
+ [Wallet changes](#wallet-changes)
* [Changelog](#changelog)
- [v5.1](#v51)
* [Changelog](#changelog-1)

<!-- tocstop -->

Expand Down Expand Up @@ -70,3 +72,10 @@ v5.x Release notes
- \[[`9cf8cb83`](https://github.com/handshake-org/hsd/commit/9cf8cb83)] - [#776](https://github.com/handshake-org/hsd/pull/776) - **docker/docs**: Fix npm warning (@NetOpWibby)
- \[[`395878a0`](https://github.com/handshake-org/hsd/commit/395878a0)] - [#775](https://github.com/handshake-org/hsd/pull/775) - **docs**: fix doc (@Falci - Fernando Falci)
- \[[`fb5501c5`](https://github.com/handshake-org/hsd/commit/fb5501c5)] - [#765](https://github.com/handshake-org/hsd/pull/765) - **chore(BIP39)**: added Portuguese wordlist (@Falci - Fernando Falci)

# v5.1
## Changelog
- \[[`6b47c3a0`](https://github.com/handshake-org/hsd/commit/6b47c3a0)] - [#805](https://github.com/handshake-org/hsd/pull/805) - **SEMVER-MINOR network**: update last checkpoint (@nodech - Nodari Chkuaselidze)
- \[[`15f74ccf`](https://github.com/handshake-org/hsd/commit/15f74ccf)] - [#824](https://github.com/handshake-org/hsd/pull/824) - **SEMVER-MINOR rpc**: Added totalSigs to JSON response for scriptToJSON (@Nathanwoodburn - Nathan Woodburn)
- \[[`81bddcd2`](https://github.com/handshake-org/hsd/commit/81bddcd2)] - [#825](https://github.com/handshake-org/hsd/pull/825) - **wallet**: fix makeBatch to generate addresses early (@rithvikvibhu - Rithvik Vibhu)
- \[[`ed27e7f6`](https://github.com/handshake-org/hsd/commit/ed27e7f6)] - [#826](https://github.com/handshake-org/hsd/pull/826) - **mempool**: more invalidation tests. (@nodech - Nodari Chkuaselidze)
6 changes: 4 additions & 2 deletions lib/mempool/contractstate.js
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,9 @@ class ContractState {
}

handleExpired(nameHash, items) {
return this.toSet(this.updates, nameHash, items);
this.toSet(this.updates, nameHash, items);
this.toSet(this.reveals, nameHash, items);
return this;
}

handleOpen(nameHash, items) {
Expand Down Expand Up @@ -335,7 +337,7 @@ class ContractState {
const state = ns.state(nextHeight, network);

switch (state) {
case states.OPEN:
case states.OPENING:
this.handleOpen(nameHash, invalid);
break;
case states.BIDDING:
Expand Down
6 changes: 5 additions & 1 deletion lib/node/rpc.js
Original file line number Diff line number Diff line change
Expand Up @@ -2978,17 +2978,21 @@ class RPC extends RPCBase {
hex: undefined,
type: Script.typesByVal[type],
reqSigs: 1,
totalSigs: 1,
p2sh: undefined
};

if (hex)
json.hex = script.toJSON();

const [m] = script.getMultisig();
const [m, n] = script.getMultisig();

if (m !== -1)
json.reqSigs = m;

if (n !== -1)
json.totalSigs = n;

return json;
}

Expand Down
2 changes: 1 addition & 1 deletion lib/primitives/covenant.js
Original file line number Diff line number Diff line change
Expand Up @@ -618,7 +618,7 @@ class Covenant extends bio.Struct {
*/

format() {
return `<Covenant: ${this.type}:${this.toString()}>`;
return `<Covenant: ${typesByVal[this.type]}:${this.toString()}>`;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion lib/protocol/networks.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ main.checkpointMap = {
* @default
*/

main.lastCheckpoint = 100000;
main.lastCheckpoint = 130000;

/**
* Reward halving interval.
Expand Down
27 changes: 16 additions & 11 deletions lib/wallet/wallet.js
Original file line number Diff line number Diff line change
Expand Up @@ -1716,14 +1716,16 @@ class Wallet extends EventEmitter {
* @param {Number} lockup
* @param {Number|String} acct
* @param {MTX?} mtx
* @returns {MTX}
* @param {Address?} addr
* @returns {Promise<MTX>}
*/

async makeBid(name, value, lockup, acct, mtx) {
async makeBid(name, value, lockup, acct, mtx, addr) {
assert(typeof name === 'string');
assert(Number.isSafeInteger(value) && value >= 0);
assert(Number.isSafeInteger(lockup) && lockup >= 0);
assert((acct >>> 0) === acct || typeof acct === 'string');
assert(addr == null || addr instanceof Address);

if (!rules.verifyName(name))
throw new Error(`Invalid name: ${name}.`);
Expand Down Expand Up @@ -1759,7 +1761,9 @@ class Wallet extends EventEmitter {
`Bid (${value}) exceeds lockup value (${lockup}): ${name}.`
);

const addr = await this.receiveAddress(acct);
if (!addr)
addr = await this.receiveAddress(acct);

const blind = await this.generateBlind(nameHash, addr, value);

const output = new Output();
Expand Down Expand Up @@ -3713,6 +3717,11 @@ class Wallet extends EventEmitter {
}
});

// Some actions accept output addresses to avoid address reuse.
// We track that by bumping receiveIndex.
const account = await this.getAccount(acct);
let receiveIndex = account.receiveDepth - 1;

// "actions" are arrays that start with a covenant type (or meta-type)
// followed by the arguments expected by the corresponding "make" function.
for (const action of actions) {
Expand All @@ -3735,10 +3744,12 @@ class Wallet extends EventEmitter {
assert(action.length === 1, 'Bad arguments for OPEN.');
await this.makeOpen(...action, force, acct, mtx);
break;
case 'BID':
case 'BID': {
assert(action.length === 3, 'Bad arguments for BID.');
await this.makeBid(...action, acct, mtx);
const address = account.deriveReceive(receiveIndex++).getAddress();
await this.makeBid(...action, acct, mtx, address);
break;
}
case 'REVEAL':
if (action.length === 1) {
await this.makeReveal(...action, acct, mtx);
Expand Down Expand Up @@ -3807,10 +3818,6 @@ class Wallet extends EventEmitter {
// Clean up.
// 1. Some actions MUST be the ONLY action for a name.
// i.e. no duplicate OPENs or REVOKE/FINALIZE for same name in one tx.
// 2. Some outputs may reuse same address from this.receieveAddress(acct)
// We can bump those to the next receive address,
const account = await this.getAccount(acct);
let receiveIndex = account.receiveDepth - 1;
const set = new BufferSet();
for (const output of mtx.outputs) {
const {covenant} = output;
Expand All @@ -3823,12 +3830,10 @@ class Wallet extends EventEmitter {
case types.CLAIM:
case types.OPEN:
output.address = account.deriveReceive(receiveIndex++).getAddress();

assert(!set.has(nameHash), 'Duplicate name with exclusive action.');
set.add(nameHash);
break;
case types.BID:
output.address = account.deriveReceive(receiveIndex++).getAddress();
case types.REVEAL:
case types.REDEEM:
break;
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hsd",
"version": "5.0.1",
"version": "5.1.0",
"description": "Cryptocurrency bike-shed",
"license": "MIT",
"repository": "git://github.com/handshake-org/hsd.git",
Expand Down
2 changes: 2 additions & 0 deletions test/auction-reorg-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,8 @@ describe('Auction Reorg', function() {
});

describe('Claim Reorg', function() {
this.timeout(10000);

const node = createNode();
const {chain, miner, cpu, blocks} = node;

Expand Down
2 changes: 2 additions & 0 deletions test/auction-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,8 @@ describe('Auction', function() {
});

describe('Claim', function() {
this.timeout(10000);

const node = createNode();
const {chain, miner, cpu, blocks} = node;

Expand Down
2 changes: 2 additions & 0 deletions test/chain-checkpoints-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,8 @@ describe('Checkpoints', function() {
});

it('should CLAIM and REGISTER a reserved name', async () => {
this.timeout(10000);

const claim = await wallet.fakeClaim('cloudflare');

await mineBlock(null, [claim], null, 'claim');
Expand Down
1 change: 1 addition & 0 deletions test/claim-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ async function mineBlocks(n, addr) {

describe('Reserved Name Claims', function() {
this.timeout(10000);

before(async () => {
await node.open();

Expand Down
4 changes: 4 additions & 0 deletions test/mempool-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,8 @@ describe('Mempool', function() {
});

it('should handle reorg: name claim - DNSSEC timestamp', async () => {
this.timeout(10000);

// Mempool is empty
await mempool.reset();
assert.strictEqual(mempool.map.size, 0);
Expand Down Expand Up @@ -1205,6 +1207,8 @@ describe('Mempool', function() {
});

it('should handle reorg: name claim - block commitment', async () => {
this.timeout(10000);

// Mempool is empty
await mempool.reset();
assert.strictEqual(mempool.map.size, 0);
Expand Down
4 changes: 4 additions & 0 deletions test/txstart-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ describe('Disable TXs', function() {
});

it('should reject claim from mempool before txStart', async () => {
this.timeout(10000);

const claim = await wallet.fakeClaim('cloudflare');

try {
Expand Down Expand Up @@ -184,6 +186,8 @@ describe('Disable TXs', function() {
});

it('should allow claim in mempool one block before txStart', async () => {
this.timeout(10000);

const claim = await wallet.fakeClaim('cloudflare');

try {
Expand Down
129 changes: 129 additions & 0 deletions test/wallet-importnonce-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
'use strict';

const assert = require('bsert');
const FullNode = require('../lib/node/fullnode');
const Network = require('../lib/protocol/network');
const Address = require('../lib/primitives/address');
const rules = require('../lib/covenants/rules');

/** @typedef {import('../lib/wallet/wallet')} Wallet */

const network = Network.get('regtest');

const node = new FullNode({
memory: true,
network: network.type,
plugins: [require('../lib/wallet/plugin')]
});

const { wdb } = node.require('walletdb');

async function mineBlocks(n, addr) {
addr = addr ? addr : new Address().toString(network);
for (let i = 0; i < n; i++) {
const block = await node.miner.mineBlock(null, addr);
await node.chain.add(block);
}
}

describe('Wallet Import Nonce', function () {
/** @type {Wallet} */
let walletA;

/** @type {Wallet} */
let walletB;

const NAME = rules.grindName(10, 1, network);
const NAMEHASH = rules.hashName(NAME);
const BIDS = [
{ value: 1e6, lockup: 2e6, addr: undefined }, // sendbid
{ value: 2e6, lockup: 4e6, addr: undefined }, // -|sendbatch
{ value: 4e6, lockup: 8e6, addr: undefined } // -|sendbatch
];

before(async () => {
await node.ensure();
await node.open();

// Both wallets have the same seed
walletA = await wdb.create();
walletB = await wdb.create({ mnemonic: walletA.master.mnemonic });
assert.bufferEqual(walletA.master.writeKey(), walletB.master.writeKey());
});

after(async () => {
await node.close();
});

it('should fund wallet', async () => {
await mineBlocks(2, await walletA.receiveAddress());
});

it('should open an auction and advance to bidding period', async () => {
await walletA.sendOpen(NAME, false);
await mineBlocks(network.names.treeInterval + 1);
});

it('should bid with sendbid', async () => {
const bid = BIDS[0];

const bidTx = await walletA.sendBid(NAME, bid.value, bid.lockup);

// Save address for importnonce later
bid.addr = bidTx.outputs[0].address;
});

it('should bid with sendbatch', async () => {
const batch = [
['BID', NAME, BIDS[1].value, BIDS[1].lockup],
['BID', NAME, BIDS[2].value, BIDS[2].lockup]
];

const bidTx = await walletA.sendBatch(batch);

// Save address for importnonce later
for (const output of bidTx.outputs) {
if (!output.covenant.isBid())
continue;

const index = BIDS.findIndex(bid => bid.lockup === output.value);
BIDS[index].addr = output.address;
}
});

it('should verify bids were placed', async () => {
await mineBlocks(1);
const bidsA = await walletA.getBidsByName(NAME);
assert.strictEqual(bidsA.length, BIDS.length);
});

it('should not be known by other wallet', async () => {
const bidsB = await walletB.getBidsByName(NAME);
assert.strictEqual(bidsB.length, BIDS.length);

for (const bid of bidsB)
assert.strictEqual(bid.value, -1);
});

it('should be imported by other wallet', async () => {
for (const bid of BIDS)
await walletB.generateBlinds(NAMEHASH, bid.addr, bid.value);

const bidsB = await walletB.getBidsByName(NAME);
assert.strictEqual(bidsB.length, BIDS.length);

// Ensure bids have correct true bid values
for (const bid of bidsB) {
const index = BIDS.findIndex(x => x.lockup === bid.lockup);
assert.strictEqual(BIDS[index].value, bid.value);
}
});

it('should reaveal all bids from other wallet', async () => {
await mineBlocks(network.names.biddingPeriod);

const revealTx = await walletB.sendRevealAll();
const revealOutputs = revealTx.outputs.filter(out => out.covenant.isReveal());
assert.strictEqual(revealOutputs.length, BIDS.length);
});
});
2 changes: 2 additions & 0 deletions test/wallet-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2790,6 +2790,8 @@ describe('Wallet', function() {
});

it('should confirm cloudflare CLAIM', async () => {
this.timeout(10000);

// Use a fresh wallet.
const pre = await wallet.getBalance();
assert.equal(pre.tx, 0);
Expand Down

0 comments on commit 44fab68

Please sign in to comment.