From d95352858493b0b53316f8d1a9f94de274b79d56 Mon Sep 17 00:00:00 2001 From: liu weikai Date: Thu, 15 Oct 2026 13:34:58 +0800 Subject: [PATCH] fix(geocaching): retain staged edits during SD contention --- CHANGELOG.md | Bin 140588 -> 70939 bytes .../43-裸NVS入口与存储别名清单.md | 2 +- docs/releases/1.0.0.md | 21 +++++++++++++----- .../tests/test_browse_runtime.cpp | 18 +++++++++++++++ .../src/geocaching/browse_runtime.cpp | 7 ++++++ ...geocaching-network-maintenance.browser.mjs | 18 +++++++++++---- .../hostlink/geocaching_hardware_exchange.mjs | 6 +++-- 7 files changed, 59 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 03442911d889d372a59dd83e01c217aa141934e5..d59e1fb081f12e0852e75ab25d4b914e9db704e1 100644 GIT binary patch literal 70939 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Î<=EjVlfzd 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
    Zr=6H_i{ZKQ2$y3ivMXeXl#L@$ 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 + operation)); + test::file_busy_cycles = 1; + test::file_busy_remaining.fill(1); + test::file_busy_remaining[operation] = 0; + const auto hits = test::file_busy_hits[operation]; + require(rt::queueWebDraftFile(staged_id.data(), static_cast(edit_size), 0, edit_hash.data(), router.identity.public_key.data(), 7) == rt::WebReadResult::Pending, + "busy file test could not queue its edit"); + until([&] + { return test::file_busy_hits[operation] > hits; }, + "staged input did not exercise SD contention"); + test::file_busy_cycles = 0; + require(staged(staged_id, 7) == rt::WebReadResult::AlreadySaved, "SD contention rejected the staged web edit"); + require(permission(staged_id.data(), 1, router.identity.public_key.data()) == rt::WebReadResult::Ready, "busy web edit was not persisted"); + } staged_id.fill(10); test::files[rt::kWebDraftCandidatePath][0] ^= 1; require(staged(staged_id, 7) == rt::WebReadResult::HashMismatch, "corrupted web candidate was committed"); diff --git a/platform/esp/arduino_common/src/geocaching/browse_runtime.cpp b/platform/esp/arduino_common/src/geocaching/browse_runtime.cpp index 2a40449b..d727ece0 100644 --- a/platform/esp/arduino_common/src/geocaching/browse_runtime.cpp +++ b/platform/esp/arduino_common/src/geocaching/browse_runtime.cpp @@ -2945,6 +2945,13 @@ static bool advanceWebDraftFile(Session& s) if (!file.open(kWebDraftCandidatePath, "r") || file.size() != total || !file.seek(job.size) || file.read(job.bytes + job.size, count) != static_cast(count)) { + // Map/other SD readers can own the filesystem briefly. Keep the edit + // and retry this same slice; only a real read failure rejects it. + if (file.read_busy()) + { + next_step.store(millis() + 20); + return true; + } failWebDraftSave(job, WebReadResult::Unavailable); return true; } diff --git a/tests/site/geocaching-network-maintenance.browser.mjs b/tests/site/geocaching-network-maintenance.browser.mjs index 443ee4f9..c552b6b5 100644 --- a/tests/site/geocaching-network-maintenance.browser.mjs +++ b/tests/site/geocaching-network-maintenance.browser.mjs @@ -15,6 +15,8 @@ import {GeocachingHardwareExchange} from '../../tools/hostlink/geocaching_hardwa const args=process.argv.slice(2),option=name=>args[args.indexOf(name)+1],hardwarePort=args.includes('--hardware-port')?option('--hardware-port'):null; if(hardwarePort&&(!/^COM\d+$/.test(hardwarePort)||!args.includes('--serial-python')||!args.includes('--expected-author')||!/^[a-f0-9]{64}$/.test(option('--expected-author'))))throw Error('Hardware mode requires --hardware-port COMxx --serial-python --expected-author '); const resumeDraftId=args.includes('--resume-draft')?option('--resume-draft'):null; +const resumeConfirmed=args.includes('--resume-confirmed-record'); +if(resumeConfirmed&&(!resumeDraftId||!args.includes('--resume-confirmed-receipt')))throw Error('Confirmed resume requires the exact draft, signed v1 record and directory receipt'); if(resumeDraftId&&(!hardwarePort||!args.includes('--resume-name')||!/^Wio网页验收-[a-z0-9]+$/.test(option('--resume-name'))||!/^[a-f0-9]{32}$/.test(resumeDraftId)))throw Error('Resume requires explicit hardware, isolated acceptance name and ID'); const testName=hardwarePort?(resumeDraftId?option('--resume-name'):'Wio网页验收-'+Date.now().toString(36)):'真实网络网页藏宝点',updatedName=hardwarePort?testName+'-v2':'第二版网络藏宝点'; const python=process.env.TRAIL_MATE_TRACK_PYTHON??path.resolve('.codex-build/reticulum-directory/venv/'+(process.platform==='win32'?'Scripts/python.exe':'bin/python')); @@ -26,6 +28,14 @@ async function freePort(){const socket=net.createServer();await new Promise(reso const server=http.createServer(async(req,res)=>{try{const name=new URL(req.url,'http://localhost').pathname;if(name==='/trail-mate/geocaching/network.json'){res.setHeader('Content-Type','application/json');res.end(JSON.stringify({endpoint,discoverySeeds:seeds}));return;}if(!name.startsWith('/trail-mate/'))throw Error('prefix');const file=path.resolve(root,name.slice(12)+(name.endsWith('/')?'index.html':''));if(!file.startsWith(root+path.sep))throw Error('path');res.setHeader('Content-Type',({'.html':'text/html','.js':'text/javascript','.css':'text/css','.json':'application/json','.png':'image/png','.svg':'image/svg+xml','.woff2':'font/woff2','.ttf':'font/ttf'})[path.extname(file)]??'application/octet-stream');res.end(await fs.readFile(file));}catch{res.writeHead(404).end();}});await new Promise(resolve=>server.listen(0,'127.0.0.1',resolve));const origin='http://127.0.0.1:'+server.address().port; const author=await Identity.generate(),publicKey=await author.getPublicKey(),nameHash=(await Identity.fullHash(new TextEncoder().encode('lxmf.delivery'))).slice(0,10),destination=await Identity.truncatedHash(Uint8Array.from([...nameHash,...author.identityHash])),info=[publicKey,author.identityHash,destination,7]; const drafts=new Map(),transfers=new Map(),signatures=new Map(),confirmations=new Map(),signedVersions=[],calls=[];let catalog=0,signCount=0,confirmCount=0; +let resumeBaseHash=null; +if(resumeConfirmed){ + const signed=JSON.parse(await fs.readFile(option('--resume-confirmed-record'),'utf8')).map(value=>Uint8Array.from(value)),verified=await verifySigned(signed),record=verified.record; + assert.ok(equal(record[2],Uint8Array.from(Buffer.from(resumeDraftId,'hex'))));assert.equal(record[3],1);assert.equal(record[5],0);assert.equal(record[8],testName);assert.equal(record[6],310000000);assert.equal(record[7],1210000000); + assert.equal(Buffer.from(await crypto.subtle.digest('SHA-256',record[1])).toString('hex'),option('--expected-author')); + const receipt=Object.fromEntries(Object.entries(JSON.parse(await fs.readFile(option('--resume-confirmed-receipt'),'utf8'))).map(([key,value])=>[key,Uint8Array.from(value)]));await verifyPublicationReceipt([receipt.raw,receipt.signature],signed,receipt); + resumeBaseHash=verified.summary[2].slice();signedVersions.push(signed); +} const countSignatures=()=>hardware?.signCount??signCount,countConfirmations=()=>hardware?.confirmCount??confirmCount; const hex=bytes=>Buffer.from(bytes).toString('hex'); async function usbReply(raw){const frame=new FrameDecoder().push(Uint8Array.from(raw))[0];assert.ok(frame);if(frame.type===FRAME.HELLO)return [...encodeFrame(FRAME.HELLO_ACK,frame.sequence,new Uint8Array([1,0,0,2,0,4,0,0,6,...new TextEncoder().encode('T-Deck'),4,...new TextEncoder().encode('test')]))];const request=decodeManagement(frame.payload),body=request.body;calls.push(request.code);let response; @@ -48,18 +58,18 @@ async function usbReply(raw){const frame=new FrameDecoder().push(Uint8Array.from else assert.fail('Unexpected USB operation '+request.code); return [...encodeFrame(FRAME.MANAGEMENT_RESPONSE,frame.sequence,encodeManagement(request.session,request.request,0,response))];} try{ - if(hardwarePort)hardware=new GeocachingHardwareExchange({port:hardwarePort,python:option('--serial-python'),fingerprint:option('--expected-author'),namePrefix:testName,resumeDraftId,calls,signedVersions,checkpointPath:path.join(work,'signature-recovery.json')}); + if(hardwarePort)hardware=new GeocachingHardwareExchange({port:hardwarePort,python:option('--serial-python'),fingerprint:option('--expected-author'),namePrefix:testName,resumeDraftId,resumeBaseHash,calls,signedVersions,checkpointPath:path.join(work,'signature-recovery.json')}); await fs.rm(path.join(work,'result.json'),{force:true}); await fs.rm(path.join(state,'stop'),{force:true});const tcp=await freePort();service=start('directory',['tools/geocaching/browser_test_service.py','--state',state,'--port',String(tcp),'--empty','--startup-delay','0','--run-seconds','600']);const directory=await ready(service,'ready');seeds=[directory.discovery];bridge=start('bridge',['tools/geocaching/packet_bridge.py','--port','0','--tcp-port',String(tcp),'--origin',origin,'--run-seconds','600']);endpoint='ws://127.0.0.1:'+(await ready(bridge,'bridge_ready')).port; browser=await chromium.launch({channel:process.env.TRAIL_MATE_TEST_BROWSER_CHANNEL,headless:true});const context=await browser.newContext({viewport:{width:1440,height:960}});await context.exposeFunction('deviceReply',hardware?raw=>hardware.exchange(raw):usbReply);await context.addInitScript(()=>{let input;const port={async open(){this.readable=new ReadableStream({start(controller){input=controller;}});this.writable=new WritableStream({async write(bytes){input.enqueue(Uint8Array.from(await window.deviceReply([...bytes])));}});},async setSignals(){},async close(){}};Object.defineProperty(navigator,'serial',{configurable:true,value:{requestPort:async()=>port}});});await context.route('https://tile.openstreetmap.org/**',route=>route.fulfill({contentType:'image/svg+xml',body:''})); let page=await context.newPage();const errors=[];page.on('pageerror',error=>errors.push(String(error)));await page.goto(origin+'/trail-mate/geocaching/?lang=zh-Hans');await page.waitForFunction(()=>document.querySelector('#search')&&!document.querySelector('#search').disabled,{},{timeout:60000});await page.getByRole('button',{name:'草稿',exact:true}).click();await page.getByRole('button',{name:'连接设备',exact:true}).click();await page.getByText('USB 作者已验证',{exact:true}).waitFor(); - if(resumeDraftId){await page.getByRole('button',{name:'刷新设备列表',exact:true}).click();await openTestDraft(testName);assert.ok(hardware.drafts.has(resumeDraftId));} + if(resumeDraftId){if(resumeConfirmed)await page.getByRole('button',{name:'我的藏宝点',exact:true}).click();await page.getByRole('button',{name:'刷新设备列表',exact:true}).click();await openTestDraft(testName);assert.ok(hardware.drafts.has(resumeDraftId));} else{await page.getByRole('button',{name:'新建草稿',exact:true}).click();await page.getByLabel('名称',{exact:true}).fill(testName);await page.getByLabel('正文',{exact:true}).fill('网页中文正文 \n'.repeat(25));await page.getByLabel('提示',{exact:true}).fill('&< >'.repeat(80));await page.getByLabel('纬度',{exact:true}).fill('31');await page.getByLabel('经度',{exact:true}).fill('121');await page.getByRole('button',{name:'保存草稿到设备',exact:true}).click();await page.getByText('草稿已保存到设备。刷新列表可更新发布状态。',{exact:true}).waitFor({timeout:45000});} async function publishAndSync(){await page.getByRole('button',{name:'刷新 IP 目录',exact:true}).click();await page.getByRole('button',{name:'检查并确认发布',exact:true}).click();assert.equal(await page.locator('dialog[open] script').count(),0);await page.locator('dialog[open]').getByRole('button',{name:'通过 IP 网络发布',exact:true}).click();await page.locator('.geo-publication-task').getByText('发布内容与目录签名证明已归档到设备。',{exact:true}).waitFor({timeout:60000});} async function openTestDraft(name){for(let index=0;index<128;index++){await page.waitForFunction(()=>[...document.querySelectorAll('button')].some(button=>button.textContent==='刷新设备列表'&&!button.disabled),{},{timeout:45000});const row=page.locator('.geo-draft-row').filter({hasText:name});if(await row.count()){await row.click();await page.getByLabel('名称',{exact:true}).waitFor();return;}const more=page.getByRole('button',{name:'加载更多设备草稿',exact:true});if(!await more.count())throw Error('Isolated acceptance draft not present in current device catalogue');await more.click();}throw Error('Device catalogue acceptance search exceeded bounded page count');} - await publishAndSync();assert.equal(countSignatures(),1);assert.equal(countConfirmations(),1);await page.getByRole('button',{name:'我的藏宝点',exact:true}).click();await openTestDraft(testName);await page.getByLabel('名称',{exact:true}).fill(updatedName);await page.getByRole('button',{name:'保存草稿到设备',exact:true}).click();await page.getByText('草稿已保存到设备。刷新列表可更新发布状态。',{exact:true}).waitFor();await publishAndSync();await openTestDraft(updatedName);await page.getByLabel('状态',{exact:true}).selectOption('2');await page.getByRole('button',{name:'保存草稿到设备',exact:true}).click();await page.getByText('草稿已保存到设备。刷新列表可更新发布状态。',{exact:true}).waitFor();await publishAndSync();assert.equal(countSignatures(),3);assert.equal(countConfirmations(),3);assert.equal((await verifySigned(signedVersions[2])).record[5],2);if(hardware)await page.locator('.geo-publication-panel').screenshot({path:'docs/locales/zh-Hans/web-outdoor-management/images/geocaching-wio-network-archive.png'});else await page.screenshot({path:'docs/locales/zh-Hans/web-outdoor-management/images/geocaching-real-network-archive.png',fullPage:true}); + if(!resumeConfirmed){await publishAndSync();assert.equal(countSignatures(),1);assert.equal(countConfirmations(),1);}await page.getByRole('button',{name:'我的藏宝点',exact:true}).click();await openTestDraft(testName);await page.getByLabel('名称',{exact:true}).fill(updatedName);await page.getByRole('button',{name:'保存草稿到设备',exact:true}).click();await page.getByText('草稿已保存到设备。刷新列表可更新发布状态。',{exact:true}).waitFor();await publishAndSync();await openTestDraft(updatedName);await page.getByLabel('状态',{exact:true}).selectOption('2');await page.getByRole('button',{name:'保存草稿到设备',exact:true}).click();await page.getByText('草稿已保存到设备。刷新列表可更新发布状态。',{exact:true}).waitFor();await publishAndSync();assert.equal(countSignatures(),resumeConfirmed?2:3);assert.equal(countConfirmations(),resumeConfirmed?2:3);assert.equal((await verifySigned(signedVersions[2])).record[5],2);if(hardware)await page.locator('.geo-publication-panel').screenshot({path:'docs/locales/zh-Hans/web-outdoor-management/images/geocaching-wio-network-archive.png'});else await page.screenshot({path:'docs/locales/zh-Hans/web-outdoor-management/images/geocaching-real-network-archive.png',fullPage:true}); await context.grantPermissions(['clipboard-read','clipboard-write'],{origin});const expectedId=(await verifySigned(signedVersions[2])).cacheId;await page.getByRole('button',{name:'复制藏宝点 ID',exact:true}).click();assert.equal(await page.evaluate(()=>navigator.clipboard.readText()),expectedId); const priorCalls=calls.length;for(const {id} of locales){await page.locator('.management-language select').selectOption(id);await page.locator('.geo-publication-task').getByText(geocachingIpPublicationText(id).confirmed,{exact:true}).waitFor();await page.locator('.geo-publication-task').getByText(geocachingDeviceSyncText(id).confirmed,{exact:true}).waitFor();await page.setViewportSize({width:390,height:844});assert.equal(await page.evaluate(()=>document.documentElement.scrollWidth>innerWidth+1),false);assert.equal(calls.length,priorCalls);}await page.locator('.management-language select').selectOption('zh-Hans');await page.setViewportSize({width:1440,height:960}); const native=spawnSync(python,['tools/geocaching/verify_browser_publication.py','--database',path.join(state,'service','directory.sqlite')],{input:JSON.stringify(signedVersions.map(item=>item.map(part=>[...part]))),windowsHide:true,maxBuffer:1048576});assert.equal(native.status,0,String(native.stderr));const nativeCheck=JSON.parse(native.stdout);assert.equal(nativeCheck.transportDiffersFromAuthor,true); - await page.reload();await page.getByRole('button',{name:'我的藏宝点',exact:true}).click();await page.getByRole('button',{name:'连接设备',exact:true}).click();await page.getByText('USB 作者已验证',{exact:true}).waitFor();await page.locator('.geo-publication-task').getByText('发布内容与目录签名证明已归档到设备。',{exact:true}).waitFor();assert.equal(countSignatures(),3);assert.equal(countConfirmations(),3);assert.equal(await page.evaluate(()=>window.__geoNetworkInjection),undefined);assert.ok(signedVersions.every(item=>item[0].length>512));assert.ok(!calls.some(code=>[7,16,17,20,21,67,68,70].includes(code)));assert.deepEqual(errors,[]);await fs.writeFile(path.join(work,'result.json'),JSON.stringify({passed:true,actualPage:true,actualWorker:true,nativeNetwork:true,nativeWebSerial:false,usb:hardware?'physical Wio L2 via restricted browser serial API test bridge':'synthetic independent identity with real signatures',author:hardware?{fingerprint:hardware.author.fingerprint,generation:hardware.author.generation}:undefined,cacheId:expectedId,created:true,updated:true,archived:true,directoryProof:true,deviceConfirmation:true,refreshRecovery:true,signatures:countSignatures(),confirmations:countConfirmations(),rfRequests:0,configWrites:0,keyExports:0,locales:locales.length,mobileWidth:390,clipboardId:true,resourceSizedContent:true,nativeCheck},null,2));console.log('Actual Geocaching page + Worker + native network: create/update/archive/proof/device confirmation and refresh passed. USB: '+(hardware?'physical Wio L2':'synthetic')); + await page.reload();await page.getByRole('button',{name:'我的藏宝点',exact:true}).click();await page.getByRole('button',{name:'连接设备',exact:true}).click();await page.getByText('USB 作者已验证',{exact:true}).waitFor();await page.locator('.geo-publication-task').getByText('发布内容与目录签名证明已归档到设备。',{exact:true}).waitFor();assert.equal(countSignatures(),resumeConfirmed?2:3);assert.equal(countConfirmations(),resumeConfirmed?2:3);assert.equal(await page.evaluate(()=>window.__geoNetworkInjection),undefined);assert.ok(signedVersions.every(item=>item[0].length>512));assert.ok(!calls.some(code=>[7,16,17,20,21,67,68,70].includes(code)));assert.deepEqual(errors,[]);await fs.writeFile(path.join(work,'result.json'),JSON.stringify({passed:true,actualPage:true,actualWorker:true,nativeNetwork:true,nativeWebSerial:false,usb:hardware?'physical Wio L2 via restricted browser serial API test bridge':'synthetic independent identity with real signatures',author:hardware?{fingerprint:hardware.author.fingerprint,generation:hardware.author.generation}:undefined,cacheId:expectedId,created:true,resumedVerifiedV1:resumeConfirmed,updated:true,archived:true,directoryProof:true,deviceConfirmation:true,refreshRecovery:true,signatures:countSignatures(),confirmations:countConfirmations(),rfRequests:0,configWrites:0,keyExports:0,locales:locales.length,mobileWidth:390,clipboardId:true,resourceSizedContent:true,nativeCheck},null,2));console.log('Actual Geocaching page + Worker + native network: create/update/archive/proof/device confirmation and refresh passed. USB: '+(hardware?'physical Wio L2':'synthetic')); }finally{try{if(browser){const pages=browser.contexts().flatMap(context=>context.pages());for(const page of pages){await fs.writeFile(path.join(work,'page-state.json'),JSON.stringify({signatures:countSignatures(),confirmations:countConfirmations(),operations:calls,responses:hardware?.responses,status:await page.locator('.geo-maintenance-list p').first().textContent().catch(()=>null),publication:await page.locator('.geo-publication-panel').textContent().catch(()=>null)},null,2));if(hardware)await page.locator('.geo-publication-panel').screenshot({path:path.join(work,'page.png')}).catch(()=>{});else await page.screenshot({path:path.join(work,'page.png'),fullPage:true}).catch(()=>{});}}}finally{await browser?.close();try{await hardware?.close();}finally{if(service&&service.child.exitCode===null){await fs.writeFile(path.join(state,'stop'),'stop');await Promise.race([service.done,sleep(15000)]);}for(const entry of children){if(entry.child.exitCode===null)entry.child.kill();await entry.done;await fs.writeFile(path.join(work,entry.label+'.log'),entry.output);}await new Promise(resolve=>server.close(resolve));}}} diff --git a/tools/hostlink/geocaching_hardware_exchange.mjs b/tools/hostlink/geocaching_hardware_exchange.mjs index 2df0df78..7db88944 100644 --- a/tools/hostlink/geocaching_hardware_exchange.mjs +++ b/tools/hostlink/geocaching_hardware_exchange.mjs @@ -6,10 +6,12 @@ import path from 'node:path'; import {FrameDecoder,FRAME} from '../../site/device/src/hostlink.js';import {decodeManagement} from '../../site/device/src/management-protocol.js';import {decode,verifySigned,equal} from '../../site/geocaching/src/protocol.js';import {validateAuthorInfo} from '../../site/device/src/author-identity.js'; const allowed=new Set([0,1,2,3,32,33,34,35,36,37,38,64,65,66,71,72,73,74,75,76,77]),hex=value=>Buffer.from(value).toString('hex'); export class GeocachingHardwareExchange{ - constructor({port,python,fingerprint,namePrefix,resumeDraftId=null,calls=[],signedVersions=[],checkpointPath=null}){ + constructor({port,python,fingerprint,namePrefix,resumeDraftId=null,resumeBaseHash=null,calls=[],signedVersions=[],checkpointPath=null}){ if(!/^COM\d+$/.test(port)||!/^[a-f0-9]{64}$/.test(fingerprint)||!namePrefix)throw Error('Explicit port, expected author and isolated test-name prefix required'); this.fingerprint=fingerprint;this.namePrefix=namePrefix;this.calls=calls;this.signedVersions=signedVersions;this.signatures=new Set();this.confirmations=new Set();this.confirmed=new Set();this.drafts=new Set();this.transfers=new Map();this.outputs=new Map();this.completed=new Set();this.pending=null;this.queue=Promise.resolve();this.broken=null; if(resumeDraftId!==null&&(!/^[a-f0-9]{32}$/.test(resumeDraftId)||!/^Wio网页验收-[a-z0-9]+$/.test(namePrefix)))throw Error('Resume requires an explicit isolated acceptance ID and name');this.resumeDraftId=resumeDraftId; + if(resumeBaseHash!==null)assert.ok(resumeDraftId&&resumeBaseHash instanceof Uint8Array&&resumeBaseHash.length===32,'Confirmed resume requires an exact signed base'); + this.resumeBaseHash=resumeBaseHash?.slice()??null; this.checkpointPath=checkpointPath; this.child=spawn(python,[fileURLToPath(new URL('./serial_transport.py',import.meta.url)),port],{windowsHide:true,stdio:['pipe','pipe','pipe']});this.stderr='';this.child.stderr.on('data',bytes=>{this.stderr=(this.stderr+bytes).slice(-4096);}); this.done=new Promise((resolve,reject)=>{this.child.on('error',error=>{this.broken=error;this.pending?.reject(error);this.pending=null;reject(error);});this.child.on('exit',code=>{if(this.pending){this.pending.reject(Error('Hardware serial process exited: '+code));this.pending=null;}resolve(code);});});this.done.catch(()=>{}); @@ -61,7 +63,7 @@ export class GeocachingHardwareExchange{ if(code===33){const row=this.transfers.get(hex(body[0]));row.content.set(body[2],body[1]);row.offset=Math.max(row.offset,body[1]+body[2].length);} if(code===35){const row=this.transfers.get(hex(body[0]));assert.equal(row.offset,row.total);assert.ok(equal(new Uint8Array(await crypto.subtle.digest('SHA-256',row.content)),row.hash));row.sealed=true;} if(code===66)this.drafts.add(hex(body[1])); - if(code===64&&value[3]&&hex(value[3][0])===this.resumeDraftId&&!this.drafts.has(this.resumeDraftId)){const row=value[3];assert.equal(row[3],this.namePrefix);assert.equal(row[5],310000000);assert.equal(row[6],1210000000);assert.equal(row[4],0);assert.equal(row[8][0],0);assert.ok(row[2]===null||equal(row[2],this.author.publicKey));this.drafts.add(this.resumeDraftId);} + if(code===64&&value[3]&&hex(value[3][0])===this.resumeDraftId&&!this.drafts.has(this.resumeDraftId)){const row=value[3];assert.equal(row[3],this.namePrefix);assert.equal(row[5],310000000);assert.equal(row[6],1210000000);assert.equal(row[4],0);assert.equal(row[8][0],this.resumeBaseHash?1:0);if(this.resumeBaseHash){assert.equal(row[8][1],1);assert.equal(row[8][2]&7,0);assert.ok(equal(row[7],this.resumeBaseHash));}assert.ok(row[2]===null||equal(row[2],this.author.publicKey));this.drafts.add(this.resumeDraftId);} if(code===71)this.signatures.add(hex(body[4])); if(code===75)this.confirmations.add(hex(body[0])); if([75,76].includes(code)&&value[3]===1&&value[7]===1)this.confirmed.add(hex(body[0]));