import { Sha256 } from '../crypto/crypto' ; import { OpcodesCommon } from '../vm/instruction-sets/common/opcodes' ; import { Base58AddressFormatVersion , Base58AddressNetwork , decodeBase58Address , encodeBase58AddressFormat , } from './base58-address' ; import { CashAddressNetworkPrefix , CashAddressType , decodeCashAddress , encodeCashAddress , } from './cash-address' ; remarks export enum AddressType { /**
     * Pay to Public Key (P2PK). This address type is uncommon, and primarily
     * occurs in early blocks because the original satoshi implementation mined
     * rewards to P2PK addresses.
     *
     * There are no standardized address formats for representing a P2PK address.
     * Instead, most applications use the `AddressType.p2pkh` format.
     */ p2pk = 'P2PK' , /**
     * Pay to Public Key Hash (P2PKH). The most common address type. P2PKH
     * addresses lock funds using a single private key.
     */ p2pkh = 'P2PKH' , /**
     * Pay to Script Hash (P2SH). An address type which locks funds to the hash of
     * a script provided in the spending transaction. See BIP13 for details.
     */ p2sh = 'P2SH' , /**
     * This `AddressType` represents an address using an unknown or uncommon
     * locking bytecode pattern for which no standardized address formats exist.
     */ unknown = 'unknown' } /**
 * An object representing the contents of an address. This can be used to encode
 * an address or its locking bytecode.
 *
 * See `lockingBytecodeToAddressContents` for details.
 */ export interface AddressContents { type : AddressType ; payload : Uint8Array ; } param bytecode export const lockingBytecodeToAddressContents = ( bytecode ) : AddressContents => { const p2pkhLength = 25 ; if ( bytecode . length === p2pkhLength && bytecode [ 0 ] === OpcodesCommon . OP_DUP && bytecode [ 1 ] === OpcodesCommon . OP_HASH160 && bytecode [ 2 ] === OpcodesCommon . OP_PUSHBYTES_20 && bytecode [ 23 ] === OpcodesCommon . OP_EQUALVERIFY && bytecode [ 24 ] === OpcodesCommon . OP_CHECKSIG ) { const start = 3 ; const end = 23 ; return { payload : bytecode . slice ( start , end ) , type : AddressType . p2pkh } ; } const p2shLength = 23 ; if ( bytecode . length === p2shLength && bytecode [ 0 ] === OpcodesCommon . OP_HASH160 && bytecode [ 1 ] === OpcodesCommon . OP_PUSHBYTES_20 && bytecode [ 22 ] === OpcodesCommon . OP_EQUAL ) { const start = 2 ; const end = 22 ; return { payload : bytecode . slice ( start , end ) , type : AddressType . p2sh } ; } const p2pkUncompressedLength = 67 ; if ( bytecode . length === p2pkUncompressedLength && bytecode [ 0 ] === OpcodesCommon . OP_PUSHBYTES_65 && bytecode [ 66 ] === OpcodesCommon . OP_CHECKSIG ) { const start = 1 ; const end = 66 ; return { payload : bytecode . slice ( start , end ) , type : AddressType . p2pk } ; } const p2pkCompressedLength = 35 ; if ( bytecode . length === p2pkCompressedLength && bytecode [ 0 ] === OpcodesCommon . OP_PUSHBYTES_33 && bytecode [ 34 ] === OpcodesCommon . OP_CHECKSIG ) { const start = 1 ; const end = 34 ; return { payload : bytecode . slice ( start , end ) , type : AddressType . p2pk } ; } return { payload : bytecode . slice ( ) , type : AddressType . unknown , } ; } ; param addressContents export const addressContentsToLockingBytecode = ( addressContents ) => { if ( addressContents . type === AddressType . p2pkh ) { return Uint8Array . from ( [ OpcodesCommon . OP_DUP , OpcodesCommon . OP_HASH160 , OpcodesCommon . OP_PUSHBYTES_20 , ... addressContents . payload , OpcodesCommon . OP_EQUALVERIFY , OpcodesCommon . OP_CHECKSIG , ] ) ; } if ( addressContents . type === AddressType . p2sh ) { return Uint8Array . from ( [ OpcodesCommon . OP_HASH160 , OpcodesCommon . OP_PUSHBYTES_20 , ... addressContents . payload , OpcodesCommon . OP_EQUAL , ] ) ; } if ( addressContents . type === AddressType . p2pk ) { const compressedPublicKeyLength = 33 ; return addressContents . payload . length === compressedPublicKeyLength ? Uint8Array . from ( [ OpcodesCommon . OP_PUSHBYTES_33 , ... addressContents . payload , OpcodesCommon . OP_CHECKSIG , ] ) : Uint8Array . from ( [ OpcodesCommon . OP_PUSHBYTES_65 , ... addressContents . payload , OpcodesCommon . OP_CHECKSIG , ] ) ; } return addressContents . payload ; } ; param bytecode param prefix export const lockingBytecodeToCashAddress = < Prefix extends string = CashAddressNetworkPrefix > ( bytecode , prefix ) => { const contents = lockingBytecodeToAddressContents ( bytecode ) ; if ( contents . type === AddressType . p2pkh ) { return encodeCashAddress ( prefix , CashAddressType . P2PKH , contents . payload ) ; } if ( contents . type === AddressType . p2sh ) { return encodeCashAddress ( prefix , CashAddressType . P2SH , contents . payload ) ; } return contents ; } ; export enum LockingBytecodeEncodingError { unknownCashAddressType = 'This CashAddress uses an unknown address type.' } param address export const cashAddressToLockingBytecode = ( address ) => { const decoded = decodeCashAddress ( address ) ; if ( typeof decoded === 'string' ) return decoded ; if ( decoded . type === CashAddressType . P2PKH ) { return { bytecode : addressContentsToLockingBytecode ( { payload : decoded . hash , type : AddressType . p2pkh , } ) , prefix : decoded . prefix , } ; } if ( decoded . type === CashAddressType . P2SH ) { return { bytecode : addressContentsToLockingBytecode ( { payload : decoded . hash , type : AddressType . p2sh , } ) , prefix : decoded . prefix , } ; } return LockingBytecodeEncodingError . unknownCashAddressType ; } ; param sha256 param bytecode param network export const lockingBytecodeToBase58Address = ( sha256 , bytecode , network ) => { const contents = lockingBytecodeToAddressContents ( bytecode ) ; if ( contents . type === AddressType . p2pkh ) { return encodeBase58AddressFormat ( sha256 , { 'copay-bch' : Base58AddressFormatVersion . p2pkhCopayBCH , mainnet : Base58AddressFormatVersion . p2pkh , testnet : Base58AddressFormatVersion . p2pkhTestnet , } [ network ] , contents . payload ) ; } if ( contents . type === AddressType . p2sh ) { return encodeBase58AddressFormat ( sha256 , { 'copay-bch' : Base58AddressFormatVersion . p2shCopayBCH , mainnet : Base58AddressFormatVersion . p2sh , testnet : Base58AddressFormatVersion . p2shTestnet , } [ network ] , contents . payload ) ; } return contents ; } ; param address export const base58AddressToLockingBytecode = ( sha256 , address ) => { const decoded = decodeBase58Address ( sha256 , address ) ; if ( typeof decoded === 'string' ) return decoded ; return { bytecode : addressContentsToLockingBytecode ( { payload : decoded . payload , type : [ Base58AddressFormatVersion . p2pkh , Base58AddressFormatVersion . p2pkhCopayBCH , Base58AddressFormatVersion . p2pkhTestnet , ] . includes ( decoded . version ) ? AddressType . p2pkh : AddressType . p2sh , } ) , version : decoded . version , } ; } ;