import { AssetAddress , H256 , PlatformAddress , PlatformAddressValue , U64Value } from "codechain-primitives" ; import { AssetTransferInput , Order , SignedTransaction , Transaction , U64 , UnwrapCCC } from "../core/classes" ; import { AssetTransaction } from "../core/Transaction" ; import { TransferAsset } from "../core/transaction/TransferAsset" ; import { NetworkId } from "../core/types" ; import { SignatureTag } from "../utils" ; import { KeyStore } from "./KeyStore" ; import { LocalKeyStore } from "./LocalKeyStore" ; import { MemoryKeyStore } from "./MemoryKeyStore" ; import { P2PKH } from "./P2PKH" ; import { P2PKHBurn } from "./P2PKHBurn" ; import { RemoteKeyStore } from "./RemoteKeyStore" ; export type KeyStoreType = "local" | "memory" | { type : "remote" ; url : string ; } | { type : "local" ; path : string ; } ; export class Key { public static classes = { RemoteKeyStore , LocalKeyStore } ; public classes = Key . classes ; private networkId : NetworkId ; private keyStore : KeyStore | null ; private keyStoreType : KeyStoreType ; constructor ( options ) { const { networkId , keyStoreType } = options ; if ( ! isKeyStoreType ( keyStoreType ) ) { throw Error ( `Unexpected keyStoreType param: ${ keyStoreType }` ) ; } this . networkId = networkId ; this . keyStore = null ; this . keyStoreType = keyStoreType ; } param keystoreURL public createRemoteKeyStore ( keystoreURL ) { return RemoteKeyStore . create ( keystoreURL ) ; } param dbPath public createLocalKeyStore ( dbPath ? ) { return LocalKeyStore . create ( { dbPath } ) ; } param params . keyStore returns public async createPlatformAddress ( params = { } ) { const { keyStore = await this . ensureKeyStore ( ) , passphrase } = params ; if ( ! isKeyStore ( keyStore ) ) { throw Error ( `Expected keyStore param to be a KeyStore instance but found ${ keyStore }` ) ; } const accountId = await keyStore . platform . createKey ( { passphrase } ) ; const { networkId } = this ; return PlatformAddress . fromAccountId ( accountId , { networkId } ) ; } param params . type param params . keyStore returns public async createAssetAddress ( params = { } ) { const { keyStore = await this . ensureKeyStore ( ) , type = "P2PKH" , passphrase } = params ; if ( ! isKeyStore ( keyStore ) ) { throw Error ( `Expected keyStore param to be a KeyStore instance but found ${ keyStore }` ) ; } const { networkId } = this ; if ( type === "P2PKH" ) { const p2pkh = new P2PKH ( { keyStore , networkId } ) ; return p2pkh . createAddress ( { passphrase } ) ; } else if ( type === "P2PKHBurn" ) { const p2pkhBurn = new P2PKHBurn ( { keyStore , networkId } ) ; return p2pkhBurn . createAddress ( { passphrase } ) ; } else { throw Error ( `Expected the type param of createAssetAddress to be either P2PKH or P2PKHBurn but found ${ type }` ) ; } } returns public createP2PKH ( params ) { const { keyStore } = params ; if ( ! isKeyStore ( keyStore ) ) { throw Error ( `Expected keyStore param to be a KeyStore instance but found ${ keyStore }` ) ; } const { networkId } = this ; return new P2PKH ( { keyStore , networkId } ) ; } returns public createP2PKHBurn ( params ) { const { keyStore } = params ; if ( ! isKeyStore ( keyStore ) ) { throw Error ( `Expected keyStore param to be a KeyStore instance but found ${ keyStore }` ) ; } const { networkId } = this ; return new P2PKHBurn ( { keyStore , networkId } ) ; } param transaction param params param params . keyStore param params . account param params . passphrase returns public async approveTransaction ( transaction , params ) { const { account , passphrase , keyStore = await this . ensureKeyStore ( ) } = params ; if ( ! isKeyStore ( keyStore ) ) { throw Error ( `Expected keyStore param to be a KeyStore instance but found ${ keyStore }` ) ; } if ( ! PlatformAddress . check ( account ) ) { throw Error ( `Expected account param to be a PlatformAddress value but found ${ account }` ) ; } const accountId = PlatformAddress . ensure ( account ) . getAccountId ( ) ; return await keyStore . platform . sign ( { key : accountId . value , message : transaction . tracker ( ) . value , passphrase } ) ; } param tx param params . keyStore param params . account param params . passphrase returns throws throws public async signTransaction ( tx , params ) { if ( ! ( tx instanceof Transaction ) ) { throw Error ( `Expected the first argument of signTransaction to be a Transaction instance but found ${ tx }` ) ; } const { account , passphrase , keyStore = await this . ensureKeyStore ( ) , fee , seq } = params ; if ( ! isKeyStore ( keyStore ) ) { throw Error ( `Expected keyStore param to be a KeyStore instance but found ${ keyStore }` ) ; } if ( ! PlatformAddress . check ( account ) ) { throw Error ( `Expected account param to be a PlatformAddress value but found ${ account }` ) ; } if ( ! U64 . check ( fee ) ) { throw Error ( `Expected fee param to be a U64 value but found ${ fee }` ) ; } if ( typeof seq !== "number" ) { throw Error ( `Expected seq param to be a number value but found ${ seq }` ) ; } tx . setFee ( fee ) ; tx . setSeq ( seq ) ; const accountId = PlatformAddress . ensure ( account ) . getAccountId ( ) ; const sig = await keyStore . platform . sign ( { key : accountId . value , message : tx . unsignedHash ( ) . value , passphrase } ) ; return new SignedTransaction ( tx , sig ) ; } param tx param index param params . keyStore param params . passphrase public async signTransactionInput ( tx , index , params = { } ) { const input = tx . input ( index ) ; if ( input == null ) { throw Error ( `Invalid index` ) ; } const { lockScriptHash , parameters } = input . prevOut ; if ( lockScriptHash === undefined || parameters === undefined ) { throw Error ( `Invalid transaction input` ) ; } if ( lockScriptHash . value !== P2PKH . getLockScriptHash ( ) . value ) { throw Error ( `Unexpected lock script hash` ) ; } if ( parameters . length !== 1 ) { throw Error ( `Unexpected length of parameters` ) ; } const publicKeyHash = Buffer . from ( parameters [ 0 ] ) . toString ( "hex" ) ; input . setLockScript ( P2PKH . getLockScript ( ) ) ; const { keyStore = await this . ensureKeyStore ( ) , passphrase , signatureTag = { input : "all" , output : "all" } as SignatureTag } = params ; const p2pkh = this . createP2PKH ( { keyStore } ) ; let message ; if ( tx instanceof TransferAsset ) { let flag = false ; for ( const order of tx . orders ( ) ) { const inputIndices = order . inputFromIndices . concat ( order . inputFeeIndices ) ; if ( inputIndices . indexOf ( index ) !== - 1 ) { message = order . order . hash ( ) ; flag = true ; break ; } } if ( ! flag ) { message = tx . hashWithoutScript ( { tag : signatureTag , type : "input" , index } ) ; } } else { throw Error ( `Invalid tx` ) ; } input . setUnlockScript ( await p2pkh . createUnlockScript ( publicKeyHash , message ! , { passphrase , signatureTag } ) ) ; } param input param order param params . keyStore param params . passphrase public async signTransactionInputWithOrder ( input , order , params = { } ) { const { lockScriptHash , parameters } = input . prevOut ; if ( lockScriptHash === undefined || parameters === undefined ) { throw Error ( `Invalid transaction input` ) ; } if ( lockScriptHash . value !== P2PKH . getLockScriptHash ( ) . value ) { throw Error ( `Unexpected lock script hash` ) ; } if ( parameters . length !== 1 ) { throw Error ( `Unexpected length of parameters` ) ; } const publicKeyHash = Buffer . from ( parameters [ 0 ] ) . toString ( "hex" ) ; input . setLockScript ( P2PKH . getLockScript ( ) ) ; const { keyStore = await this . ensureKeyStore ( ) , passphrase } = params ; const p2pkh = this . createP2PKH ( { keyStore } ) ; input . setUnlockScript ( await p2pkh . createUnlockScript ( publicKeyHash , order . hash ( ) , { passphrase , signatureTag : { input : "all" , output : "all" } } ) ) ; } param tx param index param params . keyStore param params . passphrase public async signTransactionBurn ( tx , index , params = { } ) { const burn = tx . burn ( index ) ; if ( burn == null ) { throw Error ( `Invalid index` ) ; } const { lockScriptHash , parameters } = burn . prevOut ; if ( lockScriptHash === undefined || parameters === undefined ) { throw Error ( `Invalid transaction burn` ) ; } if ( lockScriptHash . value !== P2PKHBurn . getLockScriptHash ( ) . value ) { throw Error ( `Unexpected lock script hash` ) ; } if ( parameters . length !== 1 ) { throw Error ( `Unexpected length of parameters` ) ; } const publicKeyHash = Buffer . from ( parameters [ 0 ] ) . toString ( "hex" ) ; burn . setLockScript ( P2PKHBurn . getLockScript ( ) ) ; const { keyStore = await this . ensureKeyStore ( ) , passphrase , signatureTag = { input : "all" , output : "all" } as SignatureTag } = params ; const p2pkhBurn = this . createP2PKHBurn ( { keyStore } ) ; burn . setUnlockScript ( await p2pkhBurn . createUnlockScript ( publicKeyHash , tx . hashWithoutScript ( { tag : signatureTag , type : "burn" , index } ) , { passphrase , signatureTag } ) ) ; } private async ensureKeyStore ( ) { if ( this . keyStore == null ) { if ( this . keyStoreType === "local" ) { this . keyStore = await LocalKeyStore . create ( ) ; } else if ( this . keyStoreType === "memory" ) { this . keyStore = await LocalKeyStore . createForTest ( ) ; } else if ( this . keyStoreType . type === "local" ) { this . keyStore = await LocalKeyStore . create ( { dbPath : this . keyStoreType . path } ) ; } else if ( this . keyStoreType . type === "remote" ) { this . keyStore = await RemoteKeyStore . create ( this . keyStoreType . url ) ; } else { throw Error ( `Unreachable` ) ; } } return this . keyStore ; } } function isKeyStore ( value ) { return ( value instanceof LocalKeyStore || value instanceof RemoteKeyStore || value instanceof MemoryKeyStore ) ; } function isKeyStoreType ( value ) { if ( typeof value === "string" ) { return value === "local" || value === "memory" ; } if ( typeof value === "object" && value != null ) { return ( ( value . type === "local" && typeof value . path === "string" ) || ( value . type === "remote" && typeof value . url === "string" ) ) ; } return false ; }