Skip to content
This repository was archived by the owner on Mar 28, 2023. It is now read-only.
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
c666b58
Ask TypeScript to accept ES2019 features like Array.flat()
Shadowfiend Nov 19, 2020
934842e
Add moment for date handling
Shadowfiend Nov 19, 2020
f2762a9
Add bin/liquidations.js liquidation lookup helper
Shadowfiend Nov 19, 2020
0eea02c
Add owner lookup utility
Shadowfiend Nov 19, 2020
573eddf
Add bin/refunds.js utility draft
Shadowfiend Nov 20, 2020
5c257cc
Add Papa Parse for CSV and xpub-lib for *pub address handling
Shadowfiend Dec 6, 2020
56d6dd1
Made owner-lookup able to be imported
Shadowfiend Dec 6, 2020
d26f4bd
Fill in initial parts of refunds script
Shadowfiend Dec 6, 2020
7b3fce8
Factor sighash and tx signing to exported functions
Shadowfiend Dec 10, 2020
3cd5e07
refunds script catches exceptions in the processor flow
Shadowfiend Dec 10, 2020
1b80dbc
In refund script args, refund becomes misfund
Shadowfiend Dec 10, 2020
cce51b0
refunds.js signDigest now depends only on keep address
Shadowfiend Dec 10, 2020
7e2efdb
Fix beneficiary address reading/sig verification
Shadowfiend Dec 10, 2020
e90e4f3
Add full misfund handling to refunds.js
Shadowfiend Dec 10, 2020
9b0e9c7
Add BitcoinHelpers.constructOneInputWitnessTransaction
Shadowfiend Dec 10, 2020
0eb2852
Bubble out exceptions from keySharesReady
Shadowfiend Dec 11, 2020
00f135f
Capture full keep-ecdsa output and report with errors
Shadowfiend Dec 11, 2020
dda2b93
Fix refunds TokenStaking usage in beneficiaryOf
Shadowfiend Dec 11, 2020
5728f57
Use case-insensitive address comparison in readBeneficiary
Shadowfiend Dec 11, 2020
0bcae67
Fix refunds beneficiary file lookup path
Shadowfiend Dec 11, 2020
1a31c25
Check for beneficiary *pubs before addresses
Shadowfiend Dec 11, 2020
2fec1e2
Add basic *pub address generation based on xpub-lib
Shadowfiend Dec 11, 2020
677b355
First attempt at a liquidation split transaction
Shadowfiend Dec 11, 2020
ea2f6e7
Comment and import cleanup for refunds.js
Shadowfiend Dec 11, 2020
10fbc8c
Add nested electrum client reuse to BitcoinHelpers
Shadowfiend Dec 11, 2020
6a66581
Use single Electrum connection for refunds script
Shadowfiend Dec 11, 2020
0e63c69
Look up funding info before building liquidation split tx
Shadowfiend Dec 11, 2020
a5a2e53
Fix misfunding error messages
Shadowfiend Dec 11, 2020
b44f580
Use *pub metadata type to generate appropriate refund address
Shadowfiend Dec 11, 2020
e987c0c
Clean up typing for refunds processing function
Shadowfiend Dec 15, 2020
777af78
Better missing key share errors in refunds
Shadowfiend Dec 15, 2020
d578f06
Refunds handle close/exit event ordering better
Shadowfiend Dec 15, 2020
3f48002
Sort refund operator info for stable beneficiary ordering
Shadowfiend Dec 15, 2020
735c62b
Fix owner lookups with TokenStakingEscrow
Shadowfiend Dec 16, 2020
46a18f2
Fix direct invocation of bin/owner-lookup.js
Shadowfiend Dec 16, 2020
d925796
Include refunds in CSV output for bin/liquidations.js
Shadowfiend Dec 16, 2020
554996c
Fix date parsing and add --debug support
Shadowfiend Dec 16, 2020
6c5694c
Fix race condition in keep-ecdsa output handling for refunds
Shadowfiend Dec 16, 2020
6796c22
Rework refunds *pub handling to generate addresses properly
Shadowfiend Dec 16, 2020
cac50d4
Drop extra console.logs in refunds.js
Shadowfiend Dec 16, 2020
268d880
Reduce default refund fee to reflect current Bitcoin fee reqs
Shadowfiend Dec 16, 2020
47618bd
Move all ElectrumClient debug output to console.debug
Shadowfiend Dec 16, 2020
4b08bdd
Add support for end block in getExistingEvent(s)
Shadowfiend Dec 16, 2020
747e5ae
Fix recognition of bech32 beneficiary addresses in refunds.js
Shadowfiend Dec 17, 2020
b91560f
Update to latest subgraph for fund recovery CSV
Shadowfiend Jan 12, 2021
7d06af3
Remove unused package
corollari Jan 26, 2021
2ad6577
Merge pull request #117 from corollari/patch-3
Shadowfiend Jan 26, 2021
b8ec119
Fix BTC address regexp for misfund addresses
Shadowfiend Jan 29, 2021
aacf98a
In refunds CSV, unavailable beneficiaries are joined by ;
Shadowfiend Jan 29, 2021
4d28dc4
Add copied stake status script
Shadowfiend Jan 29, 2021
b69a3aa
Add dry-run flag for bin/refunds.js
Shadowfiend Feb 22, 2021
308fcd1
Allow owner and grant lookup for old staking contract
Shadowfiend Mar 6, 2021
27f24cf
Copied stake status script resolves all owners and grant type
Shadowfiend Mar 6, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
259 changes: 147 additions & 112 deletions bin/commands/bitcoin.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
import { BitcoinHelpers } from "../../index.js"

import sha256 from "bcrypto/lib/sha256-browser.js"
import web3Utils from "web3-utils"
const { toBN } = web3Utils
const { digest } = sha256

export const bitcoinCommandHelp = [
Expand Down Expand Up @@ -50,84 +52,35 @@ export function parseBitcoinCommand(web3, args) {

if (extra.length === 0) {
return async tbtc => {
const toBN = web3.utils.toBN
const rawOutputScript = BitcoinHelpers.Address.toRawScript(
outputAddress
)
const rawPreviousOutpoint = Buffer.concat([
toBN(previousTransactionID).toArrayLike(Buffer, "le", 32),
toBN(previousOutputIndex).toArrayLike(Buffer, "le", 4)
])

const {
value: previousOutputValueBtc,
address: previousOutputAddress
} = await BitcoinHelpers.Transaction.getSimpleOutput(
previousTransactionID,
parseInt(previousOutputIndex, 16)
parseInt(previousOutputIndex)
)
const previousOutputValue =
const previousOutputValue = Math.round(
previousOutputValueBtc *
BitcoinHelpers.satoshisPerBtc.toNumber()
const rawPreviousOutputScript = BitcoinHelpers.Address.toRawScript(
previousOutputAddress
)
const transactionFee = await BitcoinHelpers.Transaction.estimateFee(
tbtc.depositFactory.constants()
)

const outputValue = toBN(previousOutputValue).sub(
transactionFee.divn(4)
BitcoinHelpers.satoshisPerBtc.toNumber()
)
/** @type {Buffer} */
const outputValueBytes = outputValue.toArrayLike(Buffer, "le", 8)

// Construct per BIP-143; see https://en.bitcoin.it/wiki/BIP_0143
// for more.
const sighashPreimage = Buffer.concat(
[
// version
`01000000`,
// hashPrevouts
digest(digest(rawPreviousOutpoint)),
// hashSequence(00000000)
digest(digest(Buffer.from("00000000", "hex"))),
// outpoint
rawPreviousOutpoint,
// P2wPKH script:
Buffer.concat([
// length, dup, hash160, pkh_length
Buffer.from("1976a914", "hex"),
// pkh, without prefix length info
rawPreviousOutputScript.slice(2),
// equal, checksig
Buffer.from("88ac", "hex")
]),
// 8-byte little-endian input value (= previous output value)
toBN(previousOutputValue).toArrayLike(Buffer, "le", 8),
// input nSequence
"00000000",
// hash of the single output
digest(
digest(
Buffer.concat([
// value bytes
outputValueBytes,
// length of output script
Buffer.of(rawOutputScript.byteLength),
// output script
rawOutputScript
])
)
),
// nLockTime
"00000000",
// SIG_ALL
"01000000"
].map(_ => Buffer.from(_, "hex"))
)

return digest(digest(sighashPreimage)).toString("hex")
const transactionFee = (
await BitcoinHelpers.Transaction.estimateFee(
tbtc.depositFactory.constants()
)
).muln(5)

return computeSighash(
{
transactionID: previousTransactionID,
index: previousOutputIndex
},
previousOutputValue,
previousOutputAddress,
{
value: previousOutputValue - transactionFee.toNumber(),
address: outputAddress
}
).toString("hex")
}
}
}
Expand All @@ -144,56 +97,31 @@ export function parseBitcoinCommand(web3, args) {

if (extra.length === 0) {
return async tbtc => {
const toBN = web3.utils.toBN
const rawOutputScript = BitcoinHelpers.Address.toRawScript(
outputAddress
)
const rawPreviousOutpoint = Buffer.concat([
toBN(previousTransactionID).toArrayLike(Buffer, "le", 32),
toBN(previousOutputIndex).toArrayLike(Buffer, "le", 4)
])

const {
value: previousOutputValueBtc
} = await BitcoinHelpers.Transaction.getSimpleOutput(
previousTransactionID,
parseInt(previousOutputIndex, 16)
parseInt(previousOutputIndex)
)
const previousOutputValue =
const previousOutputValue = Math.round(
previousOutputValueBtc * BitcoinHelpers.satoshisPerBtc.toNumber()
const transactionFee = await BitcoinHelpers.Transaction.estimateFee(
tbtc.depositFactory.constants()
)

const outputValue = toBN(previousOutputValue).sub(
transactionFee.divn(4)
)

const rawTransaction = BitcoinHelpers.Transaction.constructOneInputOneOutputWitnessTransaction(
rawPreviousOutpoint.toString("hex"),
// We set sequence to `0` to be able to replace by fee. It reflects
// bitcoin-spv:
// https://github.com/summa-tx/bitcoin-spv/blob/2a9d594d9b14080bdbff2a899c16ffbf40d62eef/solidity/contracts/CheckBitcoinSigs.sol#L154
0,
outputValue.toNumber(),
rawOutputScript.toString("hex")
)

const signatureR = digestSignature.slice(0, 64)
const signatureS = digestSignature.slice(64)
const publicKeyPoint = BitcoinHelpers.Address.splitPublicKey(
publicKeyString
)

const signedTransaction = BitcoinHelpers.Transaction.addWitnessSignature(
rawTransaction,
0,
signatureR,
signatureS,
BitcoinHelpers.publicKeyPointToPublicKeyString(
publicKeyPoint.x.toString("hex"),
publicKeyPoint.y.toString("hex")
const transactionFee = (
await BitcoinHelpers.Transaction.estimateFee(
tbtc.depositFactory.constants()
)
).muln(5)

const outputValue = toBN(previousOutputValue).sub(transactionFee)

const signedTransaction = constructSignedTransaction(
{
transactionID: previousTransactionID,
index: previousOutputIndex
},
digestSignature,
publicKeyString,
{ value: outputValue.toNumber(), address: outputAddress }
)

const transaction = await BitcoinHelpers.Transaction.broadcast(
Expand All @@ -210,3 +138,110 @@ export function parseBitcoinCommand(web3, args) {
// If we're here, no command matched.
return null
}

export function computeSighash(
/** @type {{ transactionID: string, index: string }} */ previousOutpoint,
/** @type {number} */ previousOutputValue,
/** @type {string} */ previousOutputAddress,
/** @type {{value: number, address: string}[]} */ ...outputs
) {
const rawPreviousOutpoint = Buffer.concat([
toBN(previousOutpoint.transactionID).toArrayLike(Buffer, "le", 32),
toBN(previousOutpoint.index).toArrayLike(Buffer, "le", 4)
])
const rawPreviousOutputScript = BitcoinHelpers.Address.toRawScript(
previousOutputAddress
)
const outputByteValues = outputs.map(({ value, address }) => ({
valueBytes: toBN(value).toArrayLike(Buffer, "le", 8),
rawOutputScript: BitcoinHelpers.Address.toRawScript(address)
}))

// Construct per BIP-143; see https://en.bitcoin.it/wiki/BIP_0143
// for more.
const preimage = Buffer.concat(
[
// version
`01000000`,
// hashPrevouts
digest(digest(rawPreviousOutpoint)),
// hashSequence(00000000)
digest(digest(Buffer.from("00000000", "hex"))),
// outpoint
rawPreviousOutpoint,
// P2wPKH script:
Buffer.concat([
// length, dup, hash160, pkh_length
Buffer.from("1976a914", "hex"),
// pkh, without prefix length info
rawPreviousOutputScript.slice(2),
// equal, checksig
Buffer.from("88ac", "hex")
]),
// 8-byte little-endian input value (= previous output value)
toBN(previousOutputValue).toArrayLike(Buffer, "le", 8),
// input nSequence
"00000000",
// hash of the outputs
digest(
digest(
Buffer.concat(
outputByteValues.flatMap(({ valueBytes, rawOutputScript }) => [
// value bytes
valueBytes,
// length of output script
Buffer.of(rawOutputScript.byteLength),
// output script
rawOutputScript
])
)
)
),
// nLockTime
"00000000",
// SIG_ALL
"01000000"
].map(_ => Buffer.from(_, "hex"))
)

return /** @type {Buffer} */ (digest(digest(preimage)))
}

export function constructSignedTransaction(
/** @type {{ transactionID: string, index: string }} */ previousOutpoint,
/** @type {string} */ sighashSignature,
/** @type {string} */ publicKeyString,
/** @type {{value: number, address: string}[]} */ ...outputs
) {
const rawPreviousOutpoint = Buffer.concat([
toBN(previousOutpoint.transactionID).toArrayLike(Buffer, "le", 32),
toBN(previousOutpoint.index).toArrayLike(Buffer, "le", 4)
])

const rawTransaction = BitcoinHelpers.Transaction.constructOneInputWitnessTransaction(
rawPreviousOutpoint.toString("hex"),
// We set sequence to `0` to be able to replace by fee. It reflects
// bitcoin-spv:
// https://github.com/summa-tx/bitcoin-spv/blob/2a9d594d9b14080bdbff2a899c16ffbf40d62eef/solidity/contracts/CheckBitcoinSigs.sol#L154
0,
...outputs.map(({ value, address }) => ({
value,
script: BitcoinHelpers.Address.toScript(address)
}))
)

const signatureR = sighashSignature.slice(0, 64)
const signatureS = sighashSignature.slice(64)
const publicKeyPoint = BitcoinHelpers.Address.splitPublicKey(publicKeyString)

return BitcoinHelpers.Transaction.addWitnessSignature(
rawTransaction,
0,
signatureR,
signatureS,
BitcoinHelpers.publicKeyPointToPublicKeyString(
publicKeyPoint.x.toString("hex"),
publicKeyPoint.y.toString("hex")
)
)
}
Loading