Support bbqr psbts (#5852)

* Support bbqr psbts

https://bbqr.org/ @nvk

* add js test for bbqr
This commit is contained in:
Andrew Camilleri
2024-03-21 10:30:23 +01:00
committed by GitHub
parent e21a8df0f3
commit 88a1d83323
6 changed files with 72 additions and 5 deletions

View File

@@ -494,6 +494,10 @@ retry:
version = Regex.Match(actual, "Original file: /npm/decimal\\.js@([0-9]+.[0-9]+.[0-9]+)/decimal\\.js").Groups[1].Value;
expected = (await (await client.GetAsync($"https://cdn.jsdelivr.net/npm/decimal.js@{version}/decimal.min.js")).Content.ReadAsStringAsync()).Trim();
EqualJsContent(expected, actual);
actual = GetFileContent("BTCPayServer", "wwwroot", "vendor", "bbqr", "bbqr.iife.js").Trim();
expected = (await (await client.GetAsync($"https://cdn.jsdelivr.net/npm/bbqr@1.0.0/dist/bbqr.iife.js")).Content.ReadAsStringAsync()).Trim();
EqualJsContent(expected, actual);
}
private void EqualJsContent(string expected, string actual)

View File

@@ -51,6 +51,12 @@
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" :style="{width: `${decoder.getProgress() * 100}%`}" id="progressbar"></div>
</div>
</div>
<div v-else-if="bbqrDecoder">
<div class="my-3">BBQR: {{bbqrDecoder.total}} parts, {{bbqrDecoder.progress * 100}}% completed</div>
<div class="progress">
<div class="progress-bar progress-bar-striped progress-bar-animated" role="progressbar" :style="{width: `${bbqrDecoder.progress * 100}%`}" id="progressbar"></div>
</div>
</div>
<div class="mt-3 text-center">
<button type="button" class="btn btn-primary me-1" v-if="qrData" v-on:click="submitData">Submit</button>
<button type="button" class="btn btn-secondary me-1" v-if="qrData" v-on:click="retry">Retry</button>
@@ -85,6 +91,7 @@ function initCameraScanningApp(title, onDataSubmit, modalId, submitOnScan = fals
noStreamApiSupport: false,
qrData: null,
decoder: null,
bbqrDecoder: null,
errorMessage: null,
successMessage: null,
camera: 0,
@@ -135,6 +142,7 @@ function initCameraScanningApp(title, onDataSubmit, modalId, submitOnScan = fals
this.successMessage = null;
this.errorMessage = null;
this.decoder = null;
this.bbqrDecoder = null;
},
close() {
if (this.modalId) {
@@ -146,12 +154,51 @@ function initCameraScanningApp(title, onDataSubmit, modalId, submitOnScan = fals
onDecode(content) {
if (this.qrData) return;
const isUR = content.toLowerCase().startsWith("ur:");
console.debug(1, content);
const isBBQr = content.startsWith("B$");
console.debug(content);
try {
if (!isUR) {
if (isBBQr){
this.decoder = null;
const total = parseInt(content.substr(4, 2), 36);
const current = parseInt(content.substr(6, 2), 36);
const format = content.substr(2,1);
const type = content.substr(3, 1);
if (!this.bbqrDecoder ||
this.bbqrDecoder.total !== total ||
this.bbqrDecoder.format !== format ||
this.bbqrDecoder.type !== type) {
this.bbqrDecoder = {
total,
format,
type,
data: new Array(total),
progress: 1/total
};
}
this.bbqrDecoder.data[current] = content;
const progress = this.bbqrDecoder.data.filter(value => value !== undefined).length / total;
this.bbqrDecoder.progress = progress;
if (progress >= 1) {
try {
const joinResult = BBQr.joinQRs(this.bbqrDecoder.data);
function buf2hex(buffer) { // buffer is an ArrayBuffer
return [...new Uint8Array(buffer)]
.map(x => x.toString(16).padStart(2, '0'))
.join('');
}
const result = buf2hex(joinResult.raw);
this.setQrData(result);
this.successMessage = `BBQr ${type} decoded`;
}catch (error){
this.errorMessage = error.message;
}
}
} else if (!isUR) {
this.setQrData(content);
} else {
this.bbqrDecoder = null;
this.decoder = this.decoder || new window.URlib.URRegistryDecoder();
if (this.decoder.receivePart(content)) {
if (this.decoder.isComplete()) {

View File

@@ -78,6 +78,7 @@
<script src="~/vendor/vuejs/vue.min.js" asp-append-version="true"></script>
<script src="~/vendor/vue-qrcode/vue-qrcode.min.js" asp-append-version="true"></script>
<script src="~/vendor/ur-registry/urlib.min.js" asp-append-version="true"></script>
<script src="~/vendor/bbqr/bbqr.iife.js" asp-append-version="true"></script>
<script src="~/vendor/vue-qrcode-reader/VueQrcodeReader.umd.min.js" asp-append-version="true"></script>
<script>

View File

@@ -30,6 +30,7 @@
<script src="~/vendor/vuejs/vue.min.js" asp-append-version="true"></script>
<script src="~/vendor/vue-qrcode/vue-qrcode.min.js" asp-append-version="true"></script>
<script src="~/vendor/ur-registry/urlib.min.js" asp-append-version="true"></script>
<script src="~/vendor/bbqr/bbqr.iife.js" asp-append-version="true"></script>
<script src="~/vendor/vue-qrcode-reader/VueQrcodeReader.umd.min.js" asp-append-version="true"></script>
<script>
@@ -39,9 +40,12 @@
const buffer = new window.URlib.Buffer.from(psbtHex, "hex");
const cryptoPSBT = new window.URlib.CryptoPSBT(buffer);
const encoder = cryptoPSBT.toUREncoder();
const bbqrSplitResult = BBQr.splitQRs(buffer, 'P', { maxVersion: 10});
const modes = {
ur: { title: "UR", fragments: encoder.encodeWhole() },
static: { title: "Static", fragments: [psbtHex] }
static: { title: "Static", fragments: [psbtHex] },
bbqr: { title: "BBQr", fragments: bbqrSplitResult.parts}
};
initQRShow({ title: "Scan the PSBT", modes });
}

View File

@@ -29,6 +29,7 @@
<script src="~/vendor/vuejs/vue.min.js" asp-append-version="true"></script>
<script src="~/vendor/vue-qrcode/vue-qrcode.min.js" asp-append-version="true"></script>
<script src="~/vendor/ur-registry/urlib.min.js" asp-append-version="true"></script>
<script src="~/vendor/bbqr/bbqr.iife.js" asp-append-version="true"></script>
<script src="~/vendor/vue-qrcode-reader/VueQrcodeReader.umd.min.js" asp-append-version="true"></script>
<script>
@@ -39,9 +40,11 @@
const buffer = new window.URlib.Buffer.from(psbtHex, "hex");
const cryptoPSBT = new window.URlib.CryptoPSBT(buffer);
const encoder = cryptoPSBT.toUREncoder();
const bbqrSplitResult = BBQr.splitQRs(buffer, 'P', { maxVersion: 10});
const modes = {
ur: { title: "UR", fragments: encoder.encodeWhole() },
static: { title: "Static", fragments: [psbtHex] }
static: { title: "Static", fragments: [psbtHex] },
bbqr: { title: "BBQr", fragments: bbqrSplitResult.parts}
};
const continueCallback = () => {
document.querySelector("#PSBTOptionsImportHeader button").click()

File diff suppressed because one or more lines are too long