Open hoonsubin opened 4 years ago
What is the lockScript: Buffer
? It could just be your locking script is bugged and unspendable.
@junderw thank you for the comment, here's the full lock script that's used in the code.
/**
* create a bitcoin lock script buffer with the given public key.
* this will lock the token for the given number of block sequence.
* if the given public key is not compressed, this function will compress it.
* @param publicKeyHex compressed BTC public key in hex string
* @param blockSequence bip68 encoded block sequence
* @param network bitcoin network the public key belongs to
*/
export function btcLockScript(publicKeyHex: string, blockSequence: number, network: bitcoinjs.Network): Buffer {
// verify block sequence value
if (blockSequence < 0) {
throw new Error('Block sequence cannot be a negative number');
}
if (!Number.isInteger(blockSequence) || !Number.isFinite(blockSequence)) {
throw new Error('Block sequence must be a valid integer, but received: ' + blockSequence);
}
if (blockSequence > 65535) {
// maximum lock time https://en.bitcoin.it/wiki/Timelock
throw new Error('Block sequence cannot be more than 65535');
}
// verify public key by converting to an address
if (!validatePublicKey(publicKeyHex, network)) {
throw new Error('Invalid public key');
}
const pubKeyBuffer = Buffer.from(compressPubKey(publicKeyHex, network), 'hex');
return bitcoinjs.script.fromASM(
`
${bitcoinjs.script.number.encode(blockSequence).toString('hex')}
OP_CHECKSEQUENCEVERIFY
OP_DROP
${pubKeyBuffer.toString('hex')}
OP_CHECKSIG
`
.trim()
.replace(/\s+/g, ' '),
);
}
and the P2SH that was used to lock the funds are generated here
/**
* creates a P2SH instance that locks the sent token for the given duration.
* the locked tokens can only be claimed by the provided public key
* @param lockDays the lock duration in days
* @param publicKey public key of the locker. This can be both compressed or uncompressed
* @param network bitcoin network the script will generate for
*/
export function getLockP2SH(lockDays: number, publicKey: string, network: bitcoinjs.Network) {
// only check lock duration boundaries for main net
if (network === bitcoinjs.networks.bitcoin) {
if (lockDays > 300 || lockDays < 30) {
throw new Error('Lock duration must be between 30 days to 300 days');
}
}
return bitcoinjs.payments.p2sh({
network: network,
redeem: {
output: btcLockScript(publicKey, daysToBlockSequence(lockDays), network),
},
});
}
I don't think the script is the issue here because it passes unit tests done with a randomly generated EC pair on regtest
What I'm trying to make
Sign a transaction that unlocks a time-locked token from a react-typescript front-end application on the browser with bitcoinjs-lib. The application will ask the user to choose an address path, timelock duration in days (encoded to bip68 for CSV lock), and request to export their public key from the ledger device. This will create a P2SH address with a QR code that the user can send the funds they wish to lock for the given duration. The UI will have a list of lock transactions. The user can click the 'create unlock TX' button to create and sign a redeem transaction to recover their funds after the lock period has ended.
What should happen
The user should be able to sign a new transaction that unlocks the transaction and output a raw signed transaction in hex, ready for propagation.
What happens
I've created multiple transaction methods for testing, but all of them will return the
Condition of use not satisfied (denied by the user?) (0x6985)
error. One test involves using thecreatePaymentTransactionNew()
method from the apps-btc package, this will return the 0x6985 error without any feedback from the device. The other method is usingsignP2SHTransaction()
and this will give theUnverified inputs update third party wallet software
message from Ledger Nano S and continuing the sign this transaction will result in the 0x6985 error.Transport
For Ledger device transport, I used WebUSB or U2F if the WebUSB instance does not connect.
Lock UTXO data
This is the full lock transaction, recorded in Bitcoin testnet
Code that shows the issue
This is where the transaction signature request is invoked
This is the functions used for the second method
This is the signer method that is compatible with bitcoinjs-lib
Comnment
I assume the issue comes from improper input or output data for the unlock transaction, but Ledger isn't giving me a useful error message, I've tried all options and I'm feeling hopeless. At this point, I just wish it was either my device's fault or some simple data misplacement. If you need more information, please tell me.