import { sm2 } from '@ont-dev/sm.js' ; import * as elliptic from 'elliptic' ; import BigInt from '../common/bigInt' ; import { KeyType } from '../crypto/KeyType' ; import { PublicKey } from '../crypto/PublicKey' ; import { ERROR_CODE } from '../error' ; import { num2hexstring , StringReader } from './../utils' ; import opcode from './opcode' ; export function comparePublicKeys ( a , b ) { if ( a . algorithm !== b . algorithm ) { return a . algorithm . hex - b . algorithm . hex ; } switch ( a . algorithm ) { case KeyType . ECDSA : const ec = new elliptic . ec ( a . parameters . curve . preset ) ; const paKey = ec . keyFromPublic ( a . key , 'hex' , true ) ; const pbKey = ec . keyFromPublic ( b . key , 'hex' , true ) ; const pa = paKey . getPublic ( ) ; const pb = pbKey . getPublic ( ) ; if ( pa . getX ( ) !== pb . getX ( ) ) { return pa . getX ( ) - pb . getX ( ) ; } else { return pa . getY ( ) - pb . getY ( ) ; } case KeyType . SM2 : const pka = new sm2 . SM2KeyPair ( ) ; const pkb = new sm2 . SM2KeyPair ( ) ; pka . _pubFromString ( a . key ) ; pkb . _pubFromString ( b . key ) ; if ( pka . getX ( ) . toString ( ) !== pkb . getX ( ) . toString ( ) ) { return Number ( pka . getX ( ) . toString ( ) ) - Number ( pkb . getX ( ) . toString ( ) ) ; } else { return Number ( pka . getY ( ) . toString ( ) ) - Number ( pkb . getY ( ) . toString ( ) ) ; } case KeyType . EDDSA : return Number ( a . key ) - Number ( b . key ) ; default : return 0 ; } } export function pushOpCode ( op ) { return num2hexstring ( op ) ; } export function pushPubKey ( pk ) { const pkStr = pk . serializeHex ( ) ; return pushBytes ( pkStr ) ; } export function pushBigInt ( num ) { if ( num === - 1 ) { return num2hexstring ( opcode . PUSHM1 ) ; } if ( num === 0 ) { return num2hexstring ( opcode . PUSH0 ) ; } if ( num > 0 && num <= 16 ) { return num2hexstring ( opcode . PUSH1 - 1 + num ) ; } return num2hexstring ( num , 8 , true ) ; } export function pushNum ( num ) { if ( num === 0 ) { return pushOpCode ( opcode . PUSH0 ) ; } else if ( num <= 16 ) { return num2hexstring ( num - 1 + opcode . PUSH1 ) ; } const bint = new BigInt ( num . toString ( ) ) ; return pushBytes ( bint . toHexstr ( ) ) ; } export function pushBytes ( hexstr ) { let result = '' ; if ( hexstr . length === 0 ) { throw new Error ( 'pushBytes error, hexstr is empty.' ) ; } const len = hexstr . length / 2 ; if ( len <= opcode . PUSHBYTES75 + 1 - opcode . PUSHBYTES1 ) { result += num2hexstring ( len + opcode . PUSHBYTES1 - 1 ) ; } else if ( len < 0x100 ) { result += num2hexstring ( opcode . PUSHDATA1 ) ; result += num2hexstring ( len ) ; } else if ( len < 0x10000 ) { result += num2hexstring ( opcode . PUSHDATA2 ) ; result += num2hexstring ( len , 2 , true ) ; } else if ( len < 0x100000000 ) { result += num2hexstring ( opcode . PUSHDATA4 ) ; result += num2hexstring ( len , 4 , true ) ; } else { throw ERROR_CODE . INVALID_PARAMS ; } result += hexstr ; return result ; } export function programFromPubKey ( pk ) { let result = '' ; result += pushPubKey ( pk ) ; result += pushOpCode ( opcode . CHECKSIG ) ; return result ; } export function programFromMultiPubKey ( pubkeys , m ) { const n = pubkeys . length ; if ( ! ( 1 <= m && m <= n && n <= 1024 ) ) { throw new Error ( 'Wrong multi-sig param' ) ; } pubkeys . sort ( comparePublicKeys ) ; let result = '' ; result += pushNum ( m ) ; for ( const pk of pubkeys ) { result += pushBytes ( pk . serializeHex ( ) ) ; } result += pushNum ( n ) ; result += pushOpCode ( opcode . CHECKMULTISIG ) ; return result ; } export function programFromParams ( sigs ) { let result = '' ; sigs . sort ( ) ; for ( const s of sigs ) { result += pushBytes ( s ) ; } return result ; } export function readOpcode ( sr ) { return parseInt ( sr . read ( 1 ) , 16 ) ; } export function readNum ( sr ) { let code ; try { code = readOpcode ( sr ) ; } catch ( err ) { return 0 ; } let num = code - opcode . PUSH1 + 1 ; if ( code === opcode . PUSH0 ) { readOpcode ( sr ) ; return 0 ; } else if ( 1 <= num && num <= 16 ) { readOpcode ( sr ) ; return num ; } const bint = BigInt . fromHexstr ( sr . readNextBytes ( ) ) ; num = parseInt ( bint . value . toString ( ) , 10 ) ; return num ; } export function readBytes ( sr ) { const code = readOpcode ( sr ) ; let keylen ; if ( code === opcode . PUSHDATA4 ) { keylen = sr . readUint32 ( ) ; } else if ( code === opcode . PUSHDATA2 ) { keylen = sr . readUint16 ( ) ; } else if ( code === opcode . PUSHDATA1 ) { keylen = sr . readUint8 ( ) ; } else if ( code <= opcode . PUSHBYTES75 && code >= opcode . PUSHBYTES1 ) { keylen = code - opcode . PUSHBYTES1 + 1 ; } else { throw new Error ( 'unexpected opcode: ' + code ) ; } return sr . read ( keylen ) ; } export function readPubKey ( sr ) { const pkStr = sr . readNextBytes ( ) ; return PublicKey . deserializeHex ( new StringReader ( pkStr ) ) ; } export function getParamsFromProgram ( hexstr ) { const sigs = [ ] ; const sr = new StringReader ( hexstr ) ; while ( ! sr . isEmpty ( ) ) { sigs . push ( readBytes ( sr ) ) ; } return sigs ; } export class ProgramInfo { M : number ; pubKeys : PublicKey [ ] ; } export function getProgramInfo ( hexstr ) { const info = new ProgramInfo ( ) ; const end = parseInt ( hexstr . substr ( - 2 , 2 ) , 16 ) ; if ( end === opcode . CHECKSIG ) { const sr = new StringReader ( hexstr ) ; const pk = readPubKey ( sr ) ; info . M = 1 ; info . pubKeys = [ pk ] ; return info ; } else if ( end === opcode . CHECKMULTISIG ) { const sr = new StringReader ( hexstr ) ; const m = parseInt ( sr . read ( 1 ) , 16 ) - opcode . PUSH1 + 1 ; const n = parseInt ( hexstr . substr ( - 4 , 2 ) , 16 ) - opcode . PUSH1 + 1 ; info . M = m ; info . pubKeys = [ ] ; for ( let i = 0 ; i < n ; i ++ ) { const key = readPubKey ( sr ) ; info . pubKeys . push ( key ) ; } return info ; } else { throw new Error ( 'Unsupported program.' ) ; } } export const pushHexString = ( param ) => { let result = '' ; const len = param . length / 2 ; if ( len <= opcode . PUSHBYTES75 ) { result += num2hexstring ( len ) ; } else if ( len < 0x100 ) { result += num2hexstring ( opcode . PUSHDATA1 ) ; result += num2hexstring ( len ) ; } else if ( len < 0x10000 ) { result += num2hexstring ( opcode . PUSHDATA2 ) ; result += num2hexstring ( len , 2 , true ) ; } else { result += num2hexstring ( opcode . PUSHDATA4 ) ; result += num2hexstring ( len , 4 , true ) ; } result += param ; return result ; } ;