UNPKG

@roochnetwork/rooch-sdk

Version:
129 lines (102 loc) 3.9 kB
// Copyright (c) RoochNetwork // SPDX-License-Identifier: Apache-2.0 import { bcs } from '../bcs/index.js' import { Bytes } from '../types/index.js' import { bytes, sha256, toHEX, concatBytes, varintByteNum } from '../utils/index.js' import { Signer } from './signer.js' import { SIGNATURE_SCHEME_TO_FLAG } from './signatureScheme.js' const BitcoinMessagePrefix = '\u0018Bitcoin Signed Message:\n' const MessageInfoPrefix = 'Rooch Transaction:\n' export class BitcoinSignMessage { readonly messagePrefix: string readonly messageInfo: string readonly txHash: Bytes constructor(txData: Bytes, messageInfo: string) { this.messagePrefix = BitcoinMessagePrefix let msg = messageInfo.startsWith(MessageInfoPrefix) ? messageInfo : MessageInfoPrefix + messageInfo msg = msg.charAt(msg.length - 1) !== '\n' ? msg + '\n' : msg this.messageInfo = msg this.txHash = txData } raw(): string { return this.messageInfo + toHEX(this.txHash) } encode(): Bytes { const msgHex = bytes('utf8', toHEX(this.txHash)) // Convert each part to Uint8Array const infoBytes = bytes('utf8', this.messageInfo) const prefixBytes = concatBytes( bytes('utf8', this.messagePrefix), varintByteNum(infoBytes.length + msgHex.length), ) // Calculate the total length const totalLength = prefixBytes.length + infoBytes.length + msgHex.length // Create a new Uint8Array to hold all the data const data = new Uint8Array(totalLength) // Copy each part into the new array let offset = 0 data.set(prefixBytes, offset) offset += prefixBytes.length data.set(infoBytes, offset) offset += infoBytes.length data.set(msgHex, offset) return data } hash(): Bytes { return sha256(this.encode()) } } export enum BuiltinAuthValidator { // ROOCH is a alias of SESSION, for backward compatibility ROOCH = 0x00, SESSION = 0x00, BITCOIN = 0x01, BITCOIN_MULTISIGN = 0x02, } export class Authenticator { readonly authValidatorId: number readonly payload: Bytes public constructor(authValidatorId: number, payload: Bytes) { this.authValidatorId = authValidatorId this.payload = payload } encode(): Bytes { return bcs.Authenticator.serialize({ authValidatorId: this.authValidatorId, payload: this.payload, }).toBytes() } static async rooch(input: Bytes, signer: Signer) { return this.session(input, signer) } static async session(input: Bytes, signer: Signer) { const signature = await signer.sign(input) const pubKeyBytes = signer.getPublicKey().toBytes() const serializedSignature = new Uint8Array(1 + signature.length + pubKeyBytes.length) serializedSignature.set([SIGNATURE_SCHEME_TO_FLAG[signer.getKeyScheme()]]) serializedSignature.set(signature, 1) serializedSignature.set(signer.getPublicKey().toBytes(), 1 + signature.length) return new Authenticator(BuiltinAuthValidator.SESSION, serializedSignature) } static async bitcoin( input: BitcoinSignMessage, signer: Signer, signWith: 'hash' | 'raw' = 'hash', ): Promise<Authenticator> { if (!input.messageInfo.startsWith(MessageInfoPrefix)) { throw Error('invalid message info') } const messageLength = bytes('utf8', input.messageInfo).length + toHEX(input.txHash).length const sign = await signer.sign(signWith === 'hash' ? input.hash() : bytes('utf8', input.raw())) const payload = bcs.BitcoinAuthPayload.serialize({ signature: sign, messagePrefix: concatBytes(bytes('utf8', input.messagePrefix), varintByteNum(messageLength)), messageInfo: bytes('utf8', input.messageInfo), publicKey: signer.getPublicKey().toBytes(), fromAddress: bytes('utf8', signer.getBitcoinAddress().toStr()), }).toBytes() return new Authenticator(BuiltinAuthValidator.BITCOIN, payload) } }