mirror of
https://github.com/benjamin-wilson/public-pool.git
synced 2026-09-29 09:05:06 -07:00
142 lines
5.6 KiB
TypeScript
142 lines
5.6 KiB
TypeScript
import { buildPayoutDistribution } from './payout-distribution';
|
|
|
|
const ADDRESS_A = 'bc1qs29kyaqqc0fkvj897ke9e5xa9utljjey0y5jjn';
|
|
const ADDRESS_B = 'bc1q99n3pu025yyu0jlywpmwzalyhm36tg5u37w20d';
|
|
const ADDRESS_C = 'bc1q7yx4d3xspf4xvssmsm4prf4mg2slkv8jtkc5l6';
|
|
|
|
describe('buildPayoutDistribution', () => {
|
|
it('should conserve the full coinbase reward across outputs', () => {
|
|
const result = buildPayoutDistribution({
|
|
addressWork: [
|
|
{ address: ADDRESS_A, creditedDifficulty: 2, acceptedShareCount: 2 },
|
|
{ address: ADDRESS_B, creditedDifficulty: 1, acceptedShareCount: 1 },
|
|
],
|
|
balances: [],
|
|
coinbaseValueSats: 1000,
|
|
feePercent: 0,
|
|
minOutputSats: 546,
|
|
coinbaseWeightBudget: 50_000,
|
|
});
|
|
|
|
expect(result.payoutOutputs.map(entry => ({
|
|
address: entry.address,
|
|
payoutSats: entry.payoutSats,
|
|
balanceAfterSats: entry.balanceAfterSats,
|
|
}))).toEqual([
|
|
{ address: ADDRESS_A, payoutSats: 667, balanceAfterSats: 0 },
|
|
{ address: ADDRESS_B, payoutSats: 333, balanceAfterSats: 0 },
|
|
]);
|
|
expect(result.distributedSats).toBe(1000);
|
|
expect(result.unallocatedRemainderSats).toBe(0);
|
|
});
|
|
|
|
it('should include small active miner payouts instead of reallocating their work', () => {
|
|
const result = buildPayoutDistribution({
|
|
addressWork: [
|
|
{ address: ADDRESS_A, creditedDifficulty: 999, acceptedShareCount: 999 },
|
|
{ address: ADDRESS_B, creditedDifficulty: 1, acceptedShareCount: 1 },
|
|
],
|
|
balances: [],
|
|
coinbaseValueSats: 1000,
|
|
feePercent: 0,
|
|
minOutputSats: 546,
|
|
coinbaseWeightBudget: 50_000,
|
|
});
|
|
|
|
const pending = result.entries.find(entry => entry.address === ADDRESS_B);
|
|
const paid = result.entries.find(entry => entry.address === ADDRESS_A);
|
|
|
|
expect(pending).toEqual(expect.objectContaining({
|
|
includedInCoinbase: true,
|
|
grossPayoutSats: 1,
|
|
payoutSats: 1,
|
|
balanceAfterSats: 0,
|
|
}));
|
|
expect(paid).toEqual(expect.objectContaining({
|
|
includedInCoinbase: true,
|
|
payoutSats: 999,
|
|
balanceAfterSats: 0,
|
|
}));
|
|
expect(result.distributedSats).toBe(1000);
|
|
expect(result.unallocatedRemainderSats).toBe(0);
|
|
});
|
|
|
|
it('should trim outputs that do not fit the coinbase weight budget', () => {
|
|
const result = buildPayoutDistribution({
|
|
addressWork: [
|
|
{ address: ADDRESS_A, creditedDifficulty: 10, acceptedShareCount: 1 },
|
|
{ address: ADDRESS_B, creditedDifficulty: 9, acceptedShareCount: 1 },
|
|
{ address: ADDRESS_C, creditedDifficulty: 8, acceptedShareCount: 1 },
|
|
],
|
|
balances: [],
|
|
coinbaseValueSats: 3000,
|
|
feePercent: 0,
|
|
minOutputSats: 546,
|
|
coinbaseWeightBudget: 850,
|
|
});
|
|
|
|
expect(result.payoutOutputs).toHaveLength(1);
|
|
expect(result.payoutOutputs[0].address).toBe(ADDRESS_A);
|
|
expect(result.payoutOutputs[0]).toEqual(expect.objectContaining({
|
|
payoutSats: 3000,
|
|
balanceAfterSats: -1889,
|
|
}));
|
|
expect(result.entries.find(entry => entry.address === ADDRESS_B))
|
|
.toEqual(expect.objectContaining({ includedInCoinbase: false, balanceAfterSats: 1000 }));
|
|
expect(result.entries.find(entry => entry.address === ADDRESS_C))
|
|
.toEqual(expect.objectContaining({ includedInCoinbase: false, balanceAfterSats: 889 }));
|
|
expect(result.distributedSats).toBe(3000);
|
|
expect(result.unallocatedRemainderSats).toBe(0);
|
|
});
|
|
|
|
it('should apply positive and negative carried balances to the next distribution', () => {
|
|
const result = buildPayoutDistribution({
|
|
addressWork: [
|
|
{ address: ADDRESS_A, creditedDifficulty: 10, acceptedShareCount: 1 },
|
|
{ address: ADDRESS_B, creditedDifficulty: 10, acceptedShareCount: 1 },
|
|
],
|
|
balances: [
|
|
{ address: ADDRESS_A, balanceSats: 100 },
|
|
{ address: ADDRESS_B, balanceSats: -100 },
|
|
],
|
|
coinbaseValueSats: 2000,
|
|
feePercent: 0,
|
|
minOutputSats: 546,
|
|
coinbaseWeightBudget: 50_000,
|
|
});
|
|
|
|
expect(result.payoutOutputs.map(entry => ({
|
|
address: entry.address,
|
|
payoutSats: entry.payoutSats,
|
|
balanceAfterSats: entry.balanceAfterSats,
|
|
}))).toEqual([
|
|
{ address: ADDRESS_A, payoutSats: 1100, balanceAfterSats: 0 },
|
|
{ address: ADDRESS_B, payoutSats: 900, balanceAfterSats: 0 },
|
|
]);
|
|
expect(result.unallocatedRemainderSats).toBe(0);
|
|
});
|
|
|
|
it('should reserve a configured fee output before miner distribution', () => {
|
|
const result = buildPayoutDistribution({
|
|
addressWork: [
|
|
{ address: ADDRESS_A, creditedDifficulty: 1, acceptedShareCount: 1 },
|
|
],
|
|
balances: [],
|
|
coinbaseValueSats: 100000,
|
|
feeAddress: ADDRESS_C,
|
|
feePercent: 2,
|
|
minOutputSats: 546,
|
|
coinbaseWeightBudget: 50_000,
|
|
});
|
|
|
|
expect(result.payoutOutputs[0]).toEqual(expect.objectContaining({
|
|
address: ADDRESS_C,
|
|
payoutSats: 2000,
|
|
}));
|
|
expect(result.payoutOutputs[1]).toEqual(expect.objectContaining({
|
|
address: ADDRESS_A,
|
|
payoutSats: 98000,
|
|
}));
|
|
});
|
|
});
|