SHA256
back32hash 5591: готово к тестам
Вроде как всё готово к тестам.
This commit is contained in:
@@ -39,6 +39,7 @@ import {
|
||||
} from './key-vault.js';
|
||||
import { createAns104DataItem, parseAns104DataItem } from './ans104-data-item.js';
|
||||
import { defaultServerWs } from '../deploy-config.js';
|
||||
import { SHINE_APP_TAG_NAME, SHINE_APP_TAG_VALUE, SHINE_CHANNEL_TAG_NAME, SHINE_TYPE_TAG_NAME } from './protocol-tags.js';
|
||||
|
||||
const BCH_SUFFIX = '001';
|
||||
const ZERO64 = '0'.repeat(64);
|
||||
@@ -896,7 +897,7 @@ function bytesToHexLower(bytes) {
|
||||
function parseStoredLineMeta(blockBytesB64, { fixedLine = false } = {}) {
|
||||
const raw = base64ToBytes(String(blockBytesB64 || ''));
|
||||
const frame = parseAns104DataItem(raw).data;
|
||||
const bodyOffset = 56;
|
||||
const bodyOffset = 88;
|
||||
const minLength = fixedLine ? bodyOffset + 40 : bodyOffset + 44;
|
||||
if (!(frame instanceof Uint8Array) || frame.length < minLength) throw new Error('Некорректный line-блок');
|
||||
const view = new DataView(frame.buffer, frame.byteOffset, frame.byteLength);
|
||||
@@ -934,14 +935,16 @@ function isLineConflictError(error) {
|
||||
]).has(String(error?.code || '').toUpperCase());
|
||||
}
|
||||
|
||||
function buildBlockPreimage({ prevBlockHashHex, blockNumber, msgType, msgSubType, msgVersion = 1, bodyBytes }) {
|
||||
function buildBlockPreimage({ prevBlockHashHex, back32HashHex, blockNumber, msgType, msgSubType, msgVersion = 1, bodyBytes }) {
|
||||
const prevHashBytes = hexToBytes(normalizeHex32(prevBlockHashHex));
|
||||
const back32HashBytes = hexToBytes(normalizeHex32(back32HashHex, ZERO64));
|
||||
const body = bodyBytes || new Uint8Array(0);
|
||||
const blockSize = 2 + 32 + 4 + 4 + 8 + 2 + 2 + 2 + body.length;
|
||||
const blockSize = 2 + 32 + 32 + 4 + 4 + 8 + 2 + 2 + 2 + body.length;
|
||||
|
||||
return concatBytes(
|
||||
int16Bytes(1),
|
||||
prevHashBytes,
|
||||
back32HashBytes,
|
||||
int32Bytes(blockSize),
|
||||
int32Bytes(blockNumber),
|
||||
int64Bytes(Math.floor(Date.now() / 1000)),
|
||||
@@ -1621,7 +1624,7 @@ export class AuthService {
|
||||
owner32,
|
||||
privateKey,
|
||||
data: preimage,
|
||||
tags: Array.isArray(ansTags) && ansTags.length ? ansTags : [{ name: 'App', value: 'test5590' }],
|
||||
tags: Array.isArray(ansTags) && ansTags.length ? ansTags : [{ name: SHINE_APP_TAG_NAME, value: SHINE_APP_TAG_VALUE }],
|
||||
});
|
||||
return this.ws.request('AddBlock', {
|
||||
blockchainName: cleanBlockchainName,
|
||||
@@ -1753,7 +1756,13 @@ export class AuthService {
|
||||
const tryAdd = async (cursor) => {
|
||||
const blockNumber = Number(cursor?.serverLastGlobalNumber ?? -1) + 1;
|
||||
const prevBlockHash = normalizeHex32(cursor?.serverLastGlobalHash, ZERO64);
|
||||
const preimage = await buildPreimage({ blockNumber, prevBlockHash, blockchainName });
|
||||
const back32Hash = blockNumber < 32
|
||||
? ZERO64
|
||||
: normalizeHex32(await this.getBlockHashByNumber(blockchainName, blockNumber - 32), ZERO64);
|
||||
if (blockNumber >= 32 && back32Hash === ZERO64) {
|
||||
throw new Error(`Не удалось получить hash блока #${blockNumber - 32} для back32Hash`);
|
||||
}
|
||||
const preimage = await buildPreimage({ blockNumber, prevBlockHash, back32Hash, blockchainName });
|
||||
return this.submitPreparedAddBlock({
|
||||
login,
|
||||
storagePwd,
|
||||
@@ -1813,19 +1822,20 @@ export class AuthService {
|
||||
return response.payload || {};
|
||||
}
|
||||
|
||||
const ansTags = [{ name: 'App', value: 'test5590' }];
|
||||
const ansTags = [{ name: SHINE_APP_TAG_NAME, value: SHINE_APP_TAG_VALUE }];
|
||||
const typeIndexTag = typeIndexTagValue(msgType);
|
||||
if (typeIndexTag) ansTags.push({ name: 't', value: typeIndexTag });
|
||||
if (typeIndexTag) ansTags.push({ name: SHINE_TYPE_TAG_NAME, value: typeIndexTag });
|
||||
const cleanChannelSlug = String(channelSlug || '').trim();
|
||||
if (cleanChannelSlug) ansTags.push({ name: 'c_test5590', value: cleanChannelSlug });
|
||||
if (cleanChannelSlug) ansTags.push({ name: SHINE_CHANNEL_TAG_NAME, value: cleanChannelSlug });
|
||||
|
||||
const { response, blockchainName } = await this.runAddBlockWithRetry({
|
||||
login: cleanLogin,
|
||||
storagePwd,
|
||||
resolveFreshState: () => this.resolveFreshBlockchainCursor(cleanLogin),
|
||||
ansTags,
|
||||
buildPreimage: async ({ blockNumber, prevBlockHash }) => buildBlockPreimage({
|
||||
buildPreimage: async ({ blockNumber, prevBlockHash, back32Hash }) => buildBlockPreimage({
|
||||
prevBlockHashHex: prevBlockHash,
|
||||
back32HashHex: back32Hash,
|
||||
blockNumber,
|
||||
msgType,
|
||||
msgSubType,
|
||||
@@ -2199,26 +2209,20 @@ export class AuthService {
|
||||
}
|
||||
|
||||
async getBlockHashByNumber(blockchainName, blockNumber) {
|
||||
const cleanBlockchainName = String(blockchainName || '').trim();
|
||||
const cleanBlockNumber = Number(blockNumber);
|
||||
try {
|
||||
const payload = await this.getMessageThread(
|
||||
{
|
||||
blockchainName: String(blockchainName || '').trim(),
|
||||
blockNumber: cleanBlockNumber,
|
||||
blockHash: ZERO64,
|
||||
},
|
||||
0,
|
||||
0,
|
||||
1
|
||||
);
|
||||
const hash = payload?.focus?.messageRef?.blockHash;
|
||||
return normalizeHex32(hash, ZERO64);
|
||||
} catch (error) {
|
||||
if (cleanBlockNumber === 0 && Number(error?.status) === 404) {
|
||||
return ZERO64;
|
||||
}
|
||||
throw error;
|
||||
if (!cleanBlockchainName || !Number.isInteger(cleanBlockNumber) || cleanBlockNumber < 0) {
|
||||
throw new Error('Некорректный blockchainName/blockNumber для GetBlockchainBlock');
|
||||
}
|
||||
const response = await this.ws.request('GetBlockchainBlock', {
|
||||
blockchainName: cleanBlockchainName,
|
||||
blockNumber: cleanBlockNumber,
|
||||
});
|
||||
if (response.status === 200) {
|
||||
return normalizeHex32(response?.payload?.blockHash, ZERO64);
|
||||
}
|
||||
if (cleanBlockNumber === 0 && Number(response?.status) === 404) return ZERO64;
|
||||
throw opError('GetBlockchainBlock', response);
|
||||
}
|
||||
|
||||
async resolveHeaderHashForBlockchain(blockchainName) {
|
||||
|
||||
@@ -2,6 +2,7 @@ import { createAns104DataItem, createAns104DataItemWithRawParts, parseAns104Data
|
||||
import { base64ToBytes, bytesToBase64 } from './crypto-utils.js';
|
||||
import { updateShineUserPdaOnSolana } from './shine-user-pda-service.js';
|
||||
import { updateEncryptedUserSecrets } from './key-vault.js';
|
||||
import { SHINE_APP_TAG_NAME, SHINE_APP_TAG_VALUE, SHINE_TYPE_TAG_NAME } from './protocol-tags.js';
|
||||
|
||||
const TE = new TextEncoder();
|
||||
const ZERO_HASH = '0'.repeat(64);
|
||||
@@ -33,23 +34,23 @@ function hexToBytes(hex) {
|
||||
function bytesToHex(bytes) { return Array.from(bytes || [], (b)=>b.toString(16).padStart(2,'0')).join(''); }
|
||||
function normalizeComment(value) { return String(value || '').trim().replace(/\r\n/g,'\n').replace(/\r/g,'\n'); }
|
||||
function readFrameTimestampMs(frame) {
|
||||
if (!(frame instanceof Uint8Array) || frame.length < 50) throw new Error('Некорректный SHiNE Frame');
|
||||
const seconds = new DataView(frame.buffer, frame.byteOffset, frame.byteLength).getBigInt64(42, false);
|
||||
if (!(frame instanceof Uint8Array) || frame.length < 82) throw new Error('Некорректный SHiNE Frame');
|
||||
const seconds = new DataView(frame.buffer, frame.byteOffset, frame.byteLength).getBigInt64(74, false);
|
||||
return Number(seconds * 1000n);
|
||||
}
|
||||
function readFrameType(frame) {
|
||||
if (!(frame instanceof Uint8Array) || frame.length < 56) throw new Error('Некорректный SHiNE Frame');
|
||||
if (!(frame instanceof Uint8Array) || frame.length < 88) throw new Error('Некорректный SHiNE Frame');
|
||||
const view=new DataView(frame.buffer,frame.byteOffset,frame.byteLength);
|
||||
return { type:view.getUint16(50,false), subType:view.getUint16(52,false) };
|
||||
return { type:view.getUint16(82,false), subType:view.getUint16(84,false) };
|
||||
}
|
||||
function readExplicitLinePrefix(frame) {
|
||||
if (!(frame instanceof Uint8Array) || frame.length < 100) throw new Error('Некорректный line Frame');
|
||||
if (!(frame instanceof Uint8Array) || frame.length < 132) throw new Error('Некорректный line Frame');
|
||||
const view=new DataView(frame.buffer,frame.byteOffset,frame.byteLength);
|
||||
return {
|
||||
lineCode:view.getInt32(56,false),
|
||||
prevLineNumber:view.getInt32(60,false),
|
||||
prevLineHash:bytesToHex(frame.slice(64,96)),
|
||||
lineSequence:view.getInt32(96,false),
|
||||
lineCode:view.getInt32(88,false),
|
||||
prevLineNumber:view.getInt32(92,false),
|
||||
prevLineHash:bytesToHex(frame.slice(96,128)),
|
||||
lineSequence:view.getInt32(128,false),
|
||||
};
|
||||
}
|
||||
function makeForkBody({ lineCode=0, prevLineNumber, prevLineHash, lineSequence, oldBlockchainKeyB64, forkPointBlock, forkPointHash, forkPointTimestampMs, parentTipBlock, parentTipHash, parentTipTimestampMs, reasonCode, comment }) {
|
||||
@@ -62,10 +63,10 @@ function makeForkBody({ lineCode=0, prevLineNumber, prevLineHash, lineSequence,
|
||||
}
|
||||
return concat(be32(lineCode),be32(prevLineNumber),hexToBytes(prevLineHash),be32(lineSequence),oldKey,be32(forkPointBlock),hexToBytes(forkPointHash),be64(forkPointTimestampMs),be32(parentTipBlock),hexToBytes(parentTipHash),be64(parentTipTimestampMs),be32(discarded),Uint8Array.of(Number(reasonCode)),be16(c.length),c);
|
||||
}
|
||||
function makeFrame({ prevHash, blockNumber, type, subType, version=1, body, timestampMs=Date.now() }) {
|
||||
function makeFrame({ prevHash, back32Hash=ZERO_HASH, blockNumber, type, subType, version=1, body, timestampMs=Date.now() }) {
|
||||
const bodyBytes=body || new Uint8Array(0);
|
||||
const size=2+32+4+4+8+2+2+2+bodyBytes.length;
|
||||
return concat(be16(1),hexToBytes(prevHash),be32(size),be32(blockNumber),be64(Math.floor(Number(timestampMs)/1000)),be16(type),be16(subType),be16(version),bodyBytes);
|
||||
const size=2+32+32+4+4+8+2+2+2+bodyBytes.length;
|
||||
return concat(be16(1),hexToBytes(prevHash),hexToBytes(back32Hash),be32(size),be32(blockNumber),be64(Math.floor(Number(timestampMs)/1000)),be16(type),be16(subType),be16(version),bodyBytes);
|
||||
}
|
||||
|
||||
export const KEY_ROTATION_REASONS = Object.freeze([
|
||||
@@ -153,8 +154,10 @@ export class KeyRotationClient {
|
||||
comment:rotation.comment,
|
||||
});
|
||||
const techTimestampMs=Number(rotation.createdAtMs)||readFrameTimestampMs(tipParsed.data)+1000;
|
||||
const techFrame=makeFrame({ prevHash:rotation.forkFromHash, blockNumber:cutoff+1, type:0, subType:TECH_FORK_SUBTYPE, version:1, body:forkBody, timestampMs:techTimestampMs });
|
||||
const techRaw=await createAns104DataItem({ owner32, privateKey, data:techFrame, tags:[{name:'App',value:'test5590'},{name:'t',value:'tech'}] });
|
||||
const techBlockNumber=cutoff+1;
|
||||
const techBack32Hash=techBlockNumber<32 ? ZERO_HASH : String((await this.getBlock(source,techBlockNumber-32)).blockHash||'');
|
||||
const techFrame=makeFrame({ prevHash:rotation.forkFromHash, back32Hash:techBack32Hash, blockNumber:techBlockNumber, type:0, subType:TECH_FORK_SUBTYPE, version:1, body:forkBody, timestampMs:techTimestampMs });
|
||||
const techRaw=await createAns104DataItem({ owner32, privateKey, data:techFrame, tags:[{name:SHINE_APP_TAG_NAME,value:SHINE_APP_TAG_VALUE},{name:SHINE_TYPE_TAG_NAME,value:'tech'}] });
|
||||
await this.addCandidate({ blockNumber:cutoff+1, prevBlockHash:rotation.forkFromHash, blockBytesB64:bytesToBase64(techRaw) });
|
||||
onProgress({ phase:'copy', current:total, total });
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
// Transport namespace UI. При следующей смене тестовой эпохи менять только эту строку в UI.
|
||||
export const SHINE_PROTOCOL_NAMESPACE = 'test5591';
|
||||
|
||||
export const SHINE_APP_TAG_NAME = 'App';
|
||||
export const SHINE_APP_TAG_VALUE = SHINE_PROTOCOL_NAMESPACE;
|
||||
export const SHINE_CHANNEL_TAG_NAME = 'c';
|
||||
export const SHINE_TYPE_TAG_NAME = 't';
|
||||
Reference in New Issue
Block a user