diff --git a/e2e_tests/camino/pchain_nomock.test.ts b/e2e_tests/camino/pchain_nomock.test.ts index eefef0388..ab45f3056 100644 --- a/e2e_tests/camino/pchain_nomock.test.ts +++ b/e2e_tests/camino/pchain_nomock.test.ts @@ -237,7 +237,6 @@ describe("Camino-PChain-Add-Validator", (): void => { (async function () { const stakeAmount: any = await pChain.getMinStake() const subnetAuthCredentials: [number, Buffer][] = [[0, pAddresses[1]]] - const nodeCredentials: [number, Buffer] = [2, pAddresses[2]] const unsignedTx: UnsignedTx = await pChain.buildAddSubnetValidatorTx( undefined, [P(addrB)], @@ -249,8 +248,11 @@ describe("Camino-PChain-Add-Validator", (): void => { createdSubnetID.value, memo, new BN(0), - subnetAuthCredentials, - nodeCredentials + { + addresses: [pAddresses[1]], + threshold: 1, + signer: subnetAuthCredentials + } ) const tx: Tx = unsignedTx.sign(pKeychain) diff --git a/examples/platformvm/buildAddSubnetValidatorTx.ts b/examples/platformvm/buildAddSubnetValidatorTx.ts index 7daa1057b..31836c372 100644 --- a/examples/platformvm/buildAddSubnetValidatorTx.ts +++ b/examples/platformvm/buildAddSubnetValidatorTx.ts @@ -77,7 +77,11 @@ const main = async (): Promise => { subnetID, memo, asOf, - subnetAuthCredentials + { + addresses: [pAddresses[3], pAddresses[1]], + threshold: 2, + signer: subnetAuthCredentials + } ) const tx: Tx = unsignedTx.sign(pKeychain) const txid: string = await pchain.issueTx(tx) diff --git a/examples/platformvm/buildCreateChainTx.ts b/examples/platformvm/buildCreateChainTx.ts index 5a05cdeae..ec277de40 100644 --- a/examples/platformvm/buildCreateChainTx.ts +++ b/examples/platformvm/buildCreateChainTx.ts @@ -88,7 +88,11 @@ const main = async (): Promise => { genesisData, memo, asOf, - subnetAuthCredentials + { + addresses: [pAddresses[3], pAddresses[1]], + threshold: 2, + signer: subnetAuthCredentials + } ) const tx: Tx = unsignedTx.sign(pKeychain) diff --git a/examples/platformvm/buildExportTx-XChain-Msig.ts b/examples/platformvm/buildExportTx-XChain-Msig.ts index 8a1f04f80..0b0d5c314 100644 --- a/examples/platformvm/buildExportTx-XChain-Msig.ts +++ b/examples/platformvm/buildExportTx-XChain-Msig.ts @@ -4,11 +4,9 @@ import { PlatformVMAPI, KeyChain, UTXOSet, - UnsignedTx, - Tx + PlatformVMConstants } from "caminojs/apis/platformvm" import { - MultisigAliasSet, MultisigKeyChain, MultisigKeyPair, OutputOwners @@ -41,8 +39,8 @@ const memo: Buffer = Buffer.from( ) const asOf: BN = new BN(0) const msigAliasArray = [ - "P-kopernikus1fq0jc8svlyazhygkj0s36qnl6s0km0h3uuc99w", - "P-kopernikus1k4przmfu79ypp4u7y98glmdpzwk0u3sc7saazy" + "P-kopernikus1z5tv4tg04kf4l9ghclw6ssek8zugs7yd65prpl" + //"P-kopernikus1t5qgr9hcmf2vxj7k0hz77kawf9yr389cxte5j0" ] let pchain: PlatformVMAPI @@ -80,12 +78,40 @@ const main = async (): Promise => { for (const msigAlias of msigAliasArray) { const platformVMUTXOResponse: any = await pchain.getUTXOs([msigAlias]) const utxoSet: UTXOSet = platformVMUTXOResponse.utxos - const unsignedTx: UnsignedTx = await pchain.buildExportTx( + + var unsignedTx = await pchain.buildExportTx( utxoSet, new BN(1000000000), xChainBlockchainID, xAddressStrings, + [[msigAlias], pAddressStrings], [msigAlias], + memo, + asOf, + locktime, + threshold + ) + + // UnsignedTx now contains additionally to the sigIdx all OutputOwners + // which must be fulfilled. This example makes 2 transactions to show + // the workflow of both variants. + + // Variant 1 -> TX can be fired directly + var tx = unsignedTx.sign(pKeychain) + var txid = await pchain.issueTx(tx) + console.log(`Success! TXID: ${txid}`) + + await new Promise((resolve) => setTimeout(resolve, 1000)) + + // Variant 2 -> Create a multisig keychain and use CaminoCredentials + + // We need to fetch UTXOs again because the previous are not longer valid + unsignedTx = await pchain.buildExportTx( + utxoSet, + new BN(1000000000), + xChainBlockchainID, + xAddressStrings, + [[msigAlias], pAddressStrings], [msigAlias], memo, asOf, @@ -93,14 +119,22 @@ const main = async (): Promise => { threshold ) - // Create MultiSig resolver which resolves all aliases - // If you provide addresses, no wildcard sigIndices are created + // Create the hash from the tx + const txbuff = unsignedTx.toBuffer() + const msg: Buffer = Buffer.from( + createHash("sha256").update(txbuff).digest() + ) - // MultiSigAliasSet + // Create the Multisig keychain const msigAliasBuffer = pchain.parseAddress(msigAlias) const owner = await pchain.getMultisigAlias(msigAlias) - var msSet = new MultisigAliasSet( + const msKeyChain = new MultisigKeyChain( + avalanche.getHRP(), + PChainAlias, + msg, + PlatformVMConstants.SECPMULTISIGCREDENTIAL, + unsignedTx.getTransaction().getOutputOwners(), new Map([ [ msigAliasBuffer.toString("hex"), @@ -110,22 +144,7 @@ const main = async (): Promise => { owner.threshold ) ] - ]), - new Set(pAddresses.map((a) => a.toString("hex"))) - ) - // Note: inplace modifying of input indices - unsignedTx.getTransaction().resolveMultisigIndices(msSet) - - // Create the hash from the tx - const txbuff = unsignedTx.toBuffer() - const msg: Buffer = Buffer.from( - createHash("sha256").update(txbuff).digest() - ) - - const msKeyChain = new MultisigKeyChain( - msg, - avalanche.getHRP(), - PChainAlias + ]) ) for (const address of pAddresses) { @@ -136,10 +155,15 @@ const main = async (): Promise => { msKeyChain.addKey(new MultisigKeyPair(msKeyChain, address, signature)) } + // Create signature indices (throws if not able to do so) + msKeyChain.buildSignatureIndices() + // Sign the transaction with msig keychain and issue - const tx: Tx = unsignedTx.sign(msKeyChain) - const txid: string = await pchain.issueTx(tx) + tx = unsignedTx.sign(msKeyChain) + txid = await pchain.issueTx(tx) console.log(`Success! TXID: ${txid}`) + + await new Promise((resolve) => setTimeout(resolve, 1000)) } } catch (e: any) { console.log(e) diff --git a/examples/platformvm/exportTx-AVAX-from-the-cchain-and-create-a-multisig-atomic-output.ts b/examples/platformvm/exportTx-P-C-ControlGroup.ts similarity index 100% rename from examples/platformvm/exportTx-AVAX-from-the-cchain-and-create-a-multisig-atomic-output.ts rename to examples/platformvm/exportTx-P-C-ControlGroup.ts diff --git a/examples/platformvm/importTx-avax-to-the-P-Chain-from-the-C-Chain-and-consume-a-multisig-atomic-output-and-a-create-multisig-output.ts b/examples/platformvm/importTx-C-P-ControlGroup.ts similarity index 100% rename from examples/platformvm/importTx-avax-to-the-P-Chain-from-the-C-Chain-and-consume-a-multisig-atomic-output-and-a-create-multisig-output.ts rename to examples/platformvm/importTx-C-P-ControlGroup.ts diff --git a/src/apis/platformvm/addsubnetvalidatortx.ts b/src/apis/platformvm/addsubnetvalidatortx.ts index c950e7b41..52bf082a3 100644 --- a/src/apis/platformvm/addsubnetvalidatortx.ts +++ b/src/apis/platformvm/addsubnetvalidatortx.ts @@ -7,7 +7,7 @@ import BinTools from "../../utils/bintools" import { PlatformVMConstants } from "./constants" import { TransferableOutput } from "./outputs" import { TransferableInput } from "./inputs" -import { Credential, MultisigAliasSet, SigIdx, Signature } from "../../common" +import { Credential, OutputOwners, SigIdx, Signature } from "../../common" import { BaseTx } from "./basetx" import { DefaultNetworkID } from "../../utils/constants" import { Serialization, SerializedEncoding } from "../../utils/serialization" @@ -60,8 +60,8 @@ export class AddSubnetValidatorTx extends BaseTx { protected subnetID: Buffer = Buffer.alloc(32) protected subnetAuth: SubnetAuth protected sigCount: Buffer = Buffer.alloc(4) - protected nodeSigIdx: SigIdx = undefined protected sigIdxs: SigIdx[] = [] // idxs of subnet auth signers + protected withNodeSig: boolean = false /** * Returns the id of the [[AddSubnetValidatorTx]] @@ -210,12 +210,8 @@ export class AddSubnetValidatorTx extends BaseTx { this.sigCount.writeUInt32BE(this.sigIdxs.length, 0) } - setNodeSignatureIdx(addressIdx: number, address: Buffer): void { - this.nodeSigIdx = new SigIdx() - const b: Buffer = Buffer.alloc(4) - b.writeUInt32BE(addressIdx, 0) - this.nodeSigIdx.fromBuffer(b) - this.nodeSigIdx.setSource(address) + includeNodeSignature(): void { + this.withNodeSig = true } /** @@ -234,9 +230,6 @@ export class AddSubnetValidatorTx extends BaseTx { this.subnetAuth.setAddressIndices(sigIdxs.map((idx) => idx.toBuffer())) } - getNodeSigIdx(): SigIdx { - return this.nodeSigIdx - } getCredentialID(): number { return PlatformVMConstants.SECPCREDENTIAL } @@ -252,7 +245,7 @@ export class AddSubnetValidatorTx extends BaseTx { sign(msg: Buffer, kc: KeyChain): Credential[] { const creds: Credential[] = super.sign(msg, kc) const sigidxs: SigIdx[] = this.getSigIdxs() - const cred: Credential = SelectCredentialClass(this.getCredentialID()) + let cred: Credential = SelectCredentialClass(this.getCredentialID()) for (let i: number = 0; i < sigidxs.length; i++) { const keypair: KeyPair = kc.getKey(sigidxs[`${i}`].getSource()) const signval: Buffer = keypair.sign(msg) @@ -262,28 +255,18 @@ export class AddSubnetValidatorTx extends BaseTx { } creds.push(cred) - if (this.getNodeSigIdx() != undefined) { - // sign with node sig - this.signWithNodeSig(kc, msg, creds) + if (this.withNodeSig) { + cred = cred.create() + const keypair: KeyPair = kc.getKey(this.nodeID) + const signval: Buffer = keypair.sign(msg) + const sig: Signature = new Signature() + sig.fromBuffer(signval) + cred.addSignature(sig) + creds.push(cred) } return creds } - resolveMultisigIndices(resolver: MultisigAliasSet) { - super.resolveMultisigIndices(resolver) - this.setSigIdxs(resolver.resolveMultisig(this.sigIdxs)) - } - - private signWithNodeSig(kc: KeyChain, msg: Buffer, creds: Credential[]) { - const cred: Credential = SelectCredentialClass(this.getCredentialID()) - const keypair: KeyPair = kc.getKey(this.getNodeSigIdx().getSource()) - const signval: Buffer = keypair.sign(msg) - const sig: Signature = new Signature() - sig.fromBuffer(signval) - cred.addSignature(sig) - creds.push(cred) - } - /** * Class representing an unsigned AddSubnetValidator transaction. * diff --git a/src/apis/platformvm/api.ts b/src/apis/platformvm/api.ts index 6105a7c7f..5c2bc27cc 100644 --- a/src/apis/platformvm/api.ts +++ b/src/apis/platformvm/api.ts @@ -83,7 +83,7 @@ import { TransferableInput } from "./inputs" import { TransferableOutput } from "./outputs" import { Serialization, SerializedType } from "../../utils" import { GenesisData } from "../avm" -import { LockMode, Builder } from "./builder" +import { Auth, LockMode, Builder, FromSigner } from "./builder" import { Network } from "../../utils/networks" import { Spender } from "./spender" @@ -96,6 +96,8 @@ const serialization: Serialization = Serialization.getInstance() const NanoBN = new BN(1000000000) const rewardPercentDenom = 1000000 +type FromType = String[] | String[][] + /** * Class for interacting with a node's PlatformVMAPI * @@ -1478,7 +1480,7 @@ export class PlatformVMAPI extends JRPCAPI { ownerAddresses: string[], sourceChain: Buffer | string, toAddresses: string[], - fromAddresses: string[], + fromAddresses: FromType, changeAddresses: string[] = undefined, memo: PayloadBase | Buffer = undefined, asOf: BN = ZeroBN, @@ -1486,18 +1488,16 @@ export class PlatformVMAPI extends JRPCAPI { toThreshold: number = 1, changeThreshold: number = 1 ): Promise => { - const to: Buffer[] = this._cleanAddressArray( - toAddresses, - "buildImportTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) - const from: Buffer[] = this._cleanAddressArray( - fromAddresses, - "buildImportTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) - const change: Buffer[] = this._cleanAddressArray( + const caller = "buildImportTx" + + const to: Buffer[] = this._cleanAddressArrayBuffer(toAddresses, caller) + + const fromSigner = this._parseFromSigner(fromAddresses, caller) + + const change: Buffer[] = this._cleanAddressArrayBuffer( changeAddresses, - "buildImportTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) + caller + ) let srcChain: string = undefined @@ -1531,7 +1531,7 @@ export class PlatformVMAPI extends JRPCAPI { this.core.getNetworkID(), bintools.cb58Decode(this.blockchainID), to, - from, + fromSigner, change, atomics, sourceChain, @@ -1575,7 +1575,7 @@ export class PlatformVMAPI extends JRPCAPI { amount: BN, destinationChain: Buffer | string, toAddresses: string[], - fromAddresses: string[], + fromAddresses: FromType, changeAddresses: string[] = undefined, memo: PayloadBase | Buffer = undefined, asOf: BN = ZeroBN, @@ -1583,6 +1583,8 @@ export class PlatformVMAPI extends JRPCAPI { toThreshold: number = 1, changeThreshold: number = 1 ): Promise => { + const caller = "buildExportTx" + let prefixes: object = {} toAddresses.map((a: string): void => { prefixes[a.split("-")[0]] = true @@ -1615,14 +1617,13 @@ export class PlatformVMAPI extends JRPCAPI { toAddresses.map((a: string): void => { to.push(bintools.stringToAddress(a)) }) - const from: Buffer[] = this._cleanAddressArray( - fromAddresses, - "buildExportTx" - ).map((a): Buffer => bintools.stringToAddress(a)) - const change: Buffer[] = this._cleanAddressArray( + + const fromSigner = this._parseFromSigner(fromAddresses, caller) + + const change: Buffer[] = this._cleanAddressArrayBuffer( changeAddresses, - "buildExportTx" - ).map((a): Buffer => bintools.stringToAddress(a)) + caller + ) if (memo instanceof PayloadBase) { memo = memo.getPayload() @@ -1638,7 +1639,7 @@ export class PlatformVMAPI extends JRPCAPI { amount, avaxAssetID, to, - from, + fromSigner, destinationChain, change, this.getTxFee(), @@ -1671,7 +1672,7 @@ export class PlatformVMAPI extends JRPCAPI { * @param weight The amount of weight for this subnet validator. * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} - * @param subnetAuthCredentials Optional. An array of index and address to sign for each SubnetAuth. + * @param subnetAuth Optional. An Auth struct which contains the subnet Auth and the signers. * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. @@ -1679,7 +1680,7 @@ export class PlatformVMAPI extends JRPCAPI { buildAddSubnetValidatorTx = async ( utxoset: UTXOSet, - fromAddresses: string[], + fromAddresses: FromType, changeAddresses: string[], nodeID: string, startTime: BN, @@ -1688,18 +1689,17 @@ export class PlatformVMAPI extends JRPCAPI { subnetID: string, memo: PayloadBase | Buffer = undefined, asOf: BN = ZeroBN, - subnetAuthCredentials: [number, Buffer][] = [], - nodeCredentials: [number, Buffer] = undefined, + subnetAuth: Auth = { addresses: [], threshold: 0, signer: [] }, changeThreshold: number = 1 ): Promise => { - const from: Buffer[] = this._cleanAddressArray( - fromAddresses, - "buildAddSubnetValidatorTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) - const change: Buffer[] = this._cleanAddressArray( + const caller = "buildAddSubnetValidatorTx" + + const fromSigner = this._parseFromSigner(fromAddresses, caller) + + const change: Buffer[] = this._cleanAddressArrayBuffer( changeAddresses, - "buildAddSubnetValidatorTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) + caller + ) if (memo instanceof PayloadBase) { memo = memo.getPayload() @@ -1719,7 +1719,7 @@ export class PlatformVMAPI extends JRPCAPI { ).buildAddSubnetValidatorTx( this.core.getNetworkID(), bintools.cb58Decode(this.blockchainID), - from, + fromSigner, change, NodeIDStringToBuffer(nodeID), startTime, @@ -1730,8 +1730,7 @@ export class PlatformVMAPI extends JRPCAPI { avaxAssetID, memo, asOf, - subnetAuthCredentials, - nodeCredentials, + subnetAuth, changeThreshold ) @@ -1768,7 +1767,7 @@ export class PlatformVMAPI extends JRPCAPI { buildAddDelegatorTx = async ( utxoset: UTXOSet, toAddresses: string[], - fromAddresses: string[], + fromAddresses: FromType, changeAddresses: string[], nodeID: string, startTime: BN, @@ -1782,22 +1781,19 @@ export class PlatformVMAPI extends JRPCAPI { toThreshold: number = 1, changeThreshold: number = 1 ): Promise => { - const to: Buffer[] = this._cleanAddressArray( - toAddresses, - "buildAddDelegatorTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) - const from: Buffer[] = this._cleanAddressArray( - fromAddresses, - "buildAddDelegatorTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) - const change: Buffer[] = this._cleanAddressArray( + const caller = "buildAddDelegatorTx" + const to: Buffer[] = this._cleanAddressArrayBuffer(toAddresses, caller) + + const fromSigner = this._parseFromSigner(fromAddresses, caller) + + const change: Buffer[] = this._cleanAddressArrayBuffer( changeAddresses, - "buildAddDelegatorTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) - const rewards: Buffer[] = this._cleanAddressArray( + caller + ) + const rewards: Buffer[] = this._cleanAddressArrayBuffer( rewardAddresses, - "buildAddDelegatorTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) + caller + ) if (memo instanceof PayloadBase) { memo = memo.getPayload() @@ -1833,7 +1829,7 @@ export class PlatformVMAPI extends JRPCAPI { bintools.cb58Decode(this.blockchainID), avaxAssetID, to, - from, + fromSigner, change, NodeIDStringToBuffer(nodeID), startTime, @@ -1884,7 +1880,7 @@ export class PlatformVMAPI extends JRPCAPI { buildAddValidatorTx = async ( utxoset: UTXOSet, toAddresses: string[], - fromAddresses: string[], + fromAddresses: FromType, changeAddresses: string[], nodeID: string, startTime: BN, @@ -1899,22 +1895,20 @@ export class PlatformVMAPI extends JRPCAPI { toThreshold: number = 1, changeThreshold: number = 1 ): Promise => { - const to: Buffer[] = this._cleanAddressArray( - toAddresses, - "buildAddValidatorTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) - const from: Buffer[] = this._cleanAddressArray( - fromAddresses, - "buildAddValidatorTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) - const change: Buffer[] = this._cleanAddressArray( + const caller = "buildAddValidatorTx" + + const to: Buffer[] = this._cleanAddressArrayBuffer(toAddresses, caller) + + const fromSigner = this._parseFromSigner(fromAddresses, caller) + + const change: Buffer[] = this._cleanAddressArrayBuffer( changeAddresses, - "buildAddValidatorTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) - const rewards: Buffer[] = this._cleanAddressArray( + caller + ) + const rewards: Buffer[] = this._cleanAddressArrayBuffer( rewardAddresses, - "buildAddValidatorTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) + caller + ) if (memo instanceof PayloadBase) { memo = memo.getPayload() @@ -1953,7 +1947,7 @@ export class PlatformVMAPI extends JRPCAPI { this.core.getNetworkID(), bintools.cb58Decode(this.blockchainID), to, - from, + fromSigner, change, NodeIDStringToBuffer(nodeID), startTime, @@ -1996,7 +1990,7 @@ export class PlatformVMAPI extends JRPCAPI { */ buildCreateSubnetTx = async ( utxoset: UTXOSet, - fromAddresses: string[], + fromAddresses: FromType, changeAddresses: string[], subnetOwnerAddresses: string[], subnetOwnerThreshold: number, @@ -2004,18 +1998,18 @@ export class PlatformVMAPI extends JRPCAPI { asOf: BN = ZeroBN, changeThreshold: number = 1 ): Promise => { - const from: Buffer[] = this._cleanAddressArray( - fromAddresses, - "buildCreateSubnetTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) - const change: Buffer[] = this._cleanAddressArray( + const caller = "buildCreateSubnetTx" + + const fromSigner = this._parseFromSigner(fromAddresses, caller) + + const change: Buffer[] = this._cleanAddressArrayBuffer( changeAddresses, - "buildCreateSubnetTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) - const owners: Buffer[] = this._cleanAddressArray( + caller + ) + const owners: Buffer[] = this._cleanAddressArrayBuffer( subnetOwnerAddresses, - "buildCreateSubnetTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) + caller + ) if (memo instanceof PayloadBase) { memo = memo.getPayload() @@ -2031,7 +2025,7 @@ export class PlatformVMAPI extends JRPCAPI { ).buildCreateSubnetTx( networkID, blockchainID, - from, + fromSigner, change, owners, subnetOwnerThreshold, @@ -2063,14 +2057,14 @@ export class PlatformVMAPI extends JRPCAPI { * @param genesisData Optional Byte representation of genesis state of the new chain * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} - * @param subnetAuthCredentials Optional. An array of index and address to sign for each SubnetAuth. + * @param subnetAuth Optional. An Auth struct which contains the subnet Auth and the signers. * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. */ buildCreateChainTx = async ( utxoset: UTXOSet, - fromAddresses: string[], + fromAddresses: FromType, changeAddresses: string[], subnetID: string | Buffer = undefined, chainName: string = undefined, @@ -2079,17 +2073,17 @@ export class PlatformVMAPI extends JRPCAPI { genesisData: string | GenesisData = undefined, memo: PayloadBase | Buffer = undefined, asOf: BN = ZeroBN, - subnetAuthCredentials: [number, Buffer][] = [], + subnetAuth: Auth = { addresses: [], threshold: 0, signer: [] }, changeThreshold: number = 1 ): Promise => { - const from: Buffer[] = this._cleanAddressArray( - fromAddresses, - "buildCreateChainTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) - const change: Buffer[] = this._cleanAddressArray( + const caller = "buildCreateChainTx" + + const fromSigner = this._parseFromSigner(fromAddresses, caller) + + const change: Buffer[] = this._cleanAddressArrayBuffer( changeAddresses, - "buildCreateChainTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) + caller + ) if (memo instanceof PayloadBase) { memo = memo.getPayload() @@ -2107,7 +2101,7 @@ export class PlatformVMAPI extends JRPCAPI { ).buildCreateChainTx( networkID, blockchainID, - from, + fromSigner, change, subnetID, chainName, @@ -2118,7 +2112,7 @@ export class PlatformVMAPI extends JRPCAPI { avaxAssetID, memo, asOf, - subnetAuthCredentials, + subnetAuth, changeThreshold ) @@ -2146,7 +2140,7 @@ export class PlatformVMAPI extends JRPCAPI { */ buildAddressStateTx = async ( utxoset: UTXOSet, - fromAddresses: string[], + fromAddresses: FromType, changeAddresses: string[], address: string | Buffer, state: number, @@ -2155,14 +2149,14 @@ export class PlatformVMAPI extends JRPCAPI { asOf: BN = ZeroBN, changeThreshold: number = 1 ): Promise => { - const from: Buffer[] = this._cleanAddressArray( - fromAddresses, - "buildAddressStateTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) - const change: Buffer[] = this._cleanAddressArray( + const caller = "buildAddressStateTx" + + const fromSigner = this._parseFromSigner(fromAddresses, caller) + + const change: Buffer[] = this._cleanAddressArrayBuffer( changeAddresses, - "buildAddressStateTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) + caller + ) const addressBuf = typeof address === "string" ? this.parseAddress(address) : address if (memo instanceof PayloadBase) { @@ -2179,7 +2173,7 @@ export class PlatformVMAPI extends JRPCAPI { ).buildAddressStateTx( networkID, blockchainID, - from, + fromSigner, change, addressBuf, state, @@ -2208,7 +2202,7 @@ export class PlatformVMAPI extends JRPCAPI { * @param oldNodeID Optional. ID of the existing NodeID to replace or remove. * @param newNodeID Optional. ID of the newNodID to register address. * @param address The consortiumMemberAddress, single or multi-sig. - * @param consortiumMemberAuthCredentials An array of index and address to sign for each SubnetAuth. + * @param addressAuths An array of index and address to verify ownership of address. * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO @@ -2217,24 +2211,24 @@ export class PlatformVMAPI extends JRPCAPI { */ buildRegisterNodeTx = async ( utxoset: UTXOSet, - fromAddresses: string[], + fromAddresses: FromType, changeAddresses: string[] = undefined, oldNodeID: string | Buffer = undefined, newNodeID: string | Buffer = undefined, address: string | Buffer = undefined, - consortiumMemberAuthCredentials: [number, string | Buffer][] = [], + addressAuths: [number, string | Buffer][] = [], memo: PayloadBase | Buffer = undefined, asOf: BN = ZeroBN, changeThreshold: number = 1 ): Promise => { - const from: Buffer[] = this._cleanAddressArray( - fromAddresses, - "buildRegisterNodeTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) - const change: Buffer[] = this._cleanAddressArray( + const caller = "buildRegisterNodeTx" + + const fromSigner = this._parseFromSigner(fromAddresses, caller) + + const change: Buffer[] = this._cleanAddressArrayBuffer( changeAddresses, - "buildRegisterNodeTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) + caller + ) const addrBuf = typeof address === "string" ? this.parseAddress(address) : address @@ -2242,13 +2236,21 @@ export class PlatformVMAPI extends JRPCAPI { memo = memo.getPayload() } const auth: [number, Buffer][] = [] - consortiumMemberAuthCredentials.forEach((c) => { + addressAuths.forEach((c) => { auth.push([ c[0], typeof c[1] === "string" ? this.parseAddress(c[1]) : c[1] ]) }) + if (typeof oldNodeID === "string") { + oldNodeID = NodeIDStringToBuffer(oldNodeID) + } + + if (typeof newNodeID === "string") { + newNodeID = NodeIDStringToBuffer(newNodeID) + } + const avaxAssetID: Buffer = await this.getAVAXAssetID() const networkID: number = this.core.getNetworkID() const blockchainID: Buffer = bintools.cb58Decode(this.blockchainID) @@ -2259,7 +2261,7 @@ export class PlatformVMAPI extends JRPCAPI { ).buildRegisterNodeTx( networkID, blockchainID, - from, + fromSigner, change, oldNodeID, newNodeID, @@ -2297,7 +2299,7 @@ export class PlatformVMAPI extends JRPCAPI { */ buildDepositTx = async ( utxoset: UTXOSet, - fromAddresses: string[], + fromAddresses: FromType, changeAddresses: string[] = undefined, depositOfferID: string | Buffer, depositDuration: number | Buffer, @@ -2306,14 +2308,14 @@ export class PlatformVMAPI extends JRPCAPI { asOf: BN = ZeroBN, changeThreshold: number = 1 ): Promise => { - const from: Buffer[] = this._cleanAddressArray( - fromAddresses, - "buildRegisterNodeTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) - const change: Buffer[] = this._cleanAddressArray( + const caller = "buildRegisterNodeTx" + + const fromSigner = this._parseFromSigner(fromAddresses, caller) + + const change: Buffer[] = this._cleanAddressArrayBuffer( changeAddresses, - "buildRegisterNodeTx" - ).map((a: string): Buffer => bintools.stringToAddress(a)) + caller + ) if (memo instanceof PayloadBase) { memo = memo.getPayload() @@ -2329,7 +2331,7 @@ export class PlatformVMAPI extends JRPCAPI { ).buildDepositTx( networkID, blockchainID, - from, + fromSigner, change, depositOfferID, depositDuration, @@ -2387,6 +2389,38 @@ export class PlatformVMAPI extends JRPCAPI { return addrs } + protected _cleanAddressArrayBuffer( + addresses: string[] | Buffer[], + caller: string + ): Buffer[] { + return this._cleanAddressArray(addresses, caller).map( + (a: string): Buffer => { + return typeof a === "undefined" + ? undefined + : bintools.stringToAddress(a) + } + ) + } + + protected _parseFromSigner(from: FromType, caller: string): FromSigner { + if (from.length > 0) { + if (typeof from[0] === "string") + return { + from: this._cleanAddressArrayBuffer(from as string[], caller), + signer: [] + } + else + return { + from: this._cleanAddressArrayBuffer(from[0] as string[], caller), + signer: + from.length > 1 + ? this._cleanAddressArrayBuffer(from[1] as string[], caller) + : [] + } + } + return { from: [], signer: [] } + } + /** * This class should not be instantiated directly. * Instead use the [[Avalanche.addAPI]] method. @@ -2467,6 +2501,7 @@ export class PlatformVMAPI extends JRPCAPI { */ spend = async ( from: string[] | string, + signer: string[] | string, to: string[], toThreshold: number, toLockTime: BN, @@ -2483,6 +2518,7 @@ export class PlatformVMAPI extends JRPCAPI { } const params: SpendParams = { from, + signer, to: to.length > 0 ? { @@ -2518,7 +2554,10 @@ export class PlatformVMAPI extends JRPCAPI { return { ins, - out: TransferableOutput.fromArray(Buffer.from(r.outs.slice(2), "hex")) + out: TransferableOutput.fromArray(Buffer.from(r.outs.slice(2), "hex")), + owners: r.owners + ? OutputOwners.fromArray(Buffer.from(r.owners.slice(2), "hex")) + : [] } } diff --git a/src/apis/platformvm/basetx.ts b/src/apis/platformvm/basetx.ts index af1dcec12..eeeaefe34 100644 --- a/src/apis/platformvm/basetx.ts +++ b/src/apis/platformvm/basetx.ts @@ -14,7 +14,8 @@ import { StandardBaseTx, Signature, SigIdx, - Credential + Credential, + OutputOwners } from "../../common" import { DefaultNetworkID } from "../../utils/constants" import { SelectTxClass } from "../platformvm/tx" @@ -32,6 +33,7 @@ const bintools: BinTools = BinTools.getInstance() export class BaseTx extends StandardBaseTx { protected _typeName = "BaseTx" protected _typeID = PlatformVMConstants.CREATESUBNETTX + protected _outputOwners: OutputOwners[] = undefined deserialize(fields: object, encoding: SerializedEncoding = "hex") { super.deserialize(fields, encoding) @@ -70,6 +72,23 @@ export class BaseTx extends StandardBaseTx { return PlatformVMConstants.BASETX } + /** + * @returns The outputOwners of inputs, one per input + */ + getOutputOwners(): OutputOwners[] { + if (this._outputOwners) { + return [...this._outputOwners] + } + return [] + } + + /** + * @params The outputOwners of inputs, one per input + */ + setOutputOwners(owners: OutputOwners[]) { + this._outputOwners = [...owners] + } + /** * Takes a {@link https://github.com/feross/buffer|Buffer} containing an [[BaseTx]], parses it, populates the class, and returns the length of the BaseTx in bytes. * diff --git a/src/apis/platformvm/builder.ts b/src/apis/platformvm/builder.ts index 2a25fbb6b..d86a1d36b 100644 --- a/src/apis/platformvm/builder.ts +++ b/src/apis/platformvm/builder.ts @@ -6,7 +6,7 @@ import BN from "bn.js" import { Buffer } from "buffer/" -import { OutputOwners } from "../../common/output" +import { OutputOwners, ZeroBN } from "../../common" import { DefaultNetworkID, UnixNow } from "../../utils" import { AddressError, @@ -53,6 +53,17 @@ export interface MinimumSpendable { ): Promise } +export type FromSigner = { + from: Buffer[] + signer: Buffer[] +} + +export type Auth = { + addresses: Buffer[] + threshold: number + signer: [number, Buffer][] +} + const zero: BN = new BN(0) export class Builder { @@ -73,7 +84,7 @@ export class Builder { * @param amount The amount of the asset to be spent in its smallest denomination, represented as {@link https://github.com/indutny/bn.js/|BN}. * @param assetID {@link https://github.com/feross/buffer|Buffer} of the asset ID for the UTXO * @param toAddresses The addresses to send the funds - * @param fromAddresses The addresses being used to send the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} + * @param fromSigner The addresses being used to send and verify the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} * @param changeAddresses Optional. The addresses that can spend the change remaining from the spent UTXOs. Default: toAddresses * @param fee Optional. The amount of fees to burn in its smallest denomination, represented as {@link https://github.com/indutny/bn.js/|BN} * @param feeAssetID Optional. The assetID of the fees being burned. Default: assetID @@ -92,7 +103,7 @@ export class Builder { amount: BN, amountAssetID: Buffer, toAddresses: Buffer[], - fromAddresses: Buffer[], + fromSigner: FromSigner, changeAddresses: Buffer[] = undefined, fee: BN = zero, feeAssetID: Buffer = undefined, @@ -124,7 +135,8 @@ export class Builder { const aad: AssetAmountDestination = new AssetAmountDestination( toAddresses, toThreshold, - fromAddresses, + fromSigner.from, + fromSigner.signer, changeAddresses, changeThreshold ) @@ -139,6 +151,7 @@ export class Builder { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] + let owners: OutputOwners[] = [] const minSpendableErr: Error = await this.spender.getMinimumSpendable( aad, @@ -149,11 +162,13 @@ export class Builder { if (typeof minSpendableErr === "undefined") { ins = aad.getInputs() outs = aad.getAllOutputs() + owners = aad.getOutputOwners() } else { throw minSpendableErr } const baseTx: BaseTx = new BaseTx(networkID, blockchainID, outs, ins, memo) + baseTx.setOutputOwners(owners) return new UnsignedTx(baseTx) } @@ -163,7 +178,7 @@ export class Builder { * @param networkID The number representing NetworkID of the node * @param blockchainID The {@link https://github.com/feross/buffer|Buffer} representing the BlockchainID for the transaction * @param toAddresses The addresses to send the funds - * @param fromAddresses The addresses being used to send the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} + * @param fromSigner The addresses being used to send and verify the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} * @param changeAddresses Optional. The addresses that can spend the change remaining from the spent UTXOs. Default: toAddresses * @param importIns An array of [[TransferableInput]]s being imported * @param sourceChain A {@link https://github.com/feross/buffer|Buffer} for the chainid where the imports are coming from. @@ -181,7 +196,7 @@ export class Builder { networkID: number, blockchainID: Buffer, toAddresses: Buffer[], - fromAddresses: Buffer[], + fromSigner: FromSigner, changeAddresses: Buffer[], atomics: UTXO[], sourceChain: Buffer = undefined, @@ -195,6 +210,9 @@ export class Builder { ): Promise => { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] + let owners: OutputOwners[] = [] + const importOwners: OutputOwners[] = [] + if (typeof fee === "undefined") { fee = zero.clone() } @@ -247,6 +265,13 @@ export class Builder { } xferin.getInput().addSignatureIdx(idx, spenders[`${j}`]) } + importOwners.push( + new OutputOwners( + output.getAddresses(), + output.getLocktime(), + output.getThreshold() + ) + ) importIns.push(xferin) //add extra outputs for each amount (calculated from the imported inputs), minus fees if (infeeamount.gt(zero)) { @@ -271,7 +296,8 @@ export class Builder { const aad: AssetAmountDestination = new AssetAmountDestination( toAddresses, toThreshold, - fromAddresses, + fromSigner.from, + fromSigner.signer, changeAddresses, changeThreshold ) @@ -286,12 +312,14 @@ export class Builder { if (typeof minSpendableErr === "undefined") { ins = aad.getInputs() outs = aad.getAllOutputs() + owners = aad.getOutputOwners() + owners.push(...importOwners) } else { throw minSpendableErr } } - const importTx: ImportTx = new ImportTx( + const baseTx: ImportTx = new ImportTx( networkID, blockchainID, outs, @@ -300,7 +328,9 @@ export class Builder { sourceChain, importIns ) - return new UnsignedTx(importTx) + + baseTx.setOutputOwners(owners) + return new UnsignedTx(baseTx) } /** @@ -311,7 +341,7 @@ export class Builder { * @param amount The amount being exported as a {@link https://github.com/indutny/bn.js/|BN} * @param avaxAssetID {@link https://github.com/feross/buffer|Buffer} of the asset ID for AVAX * @param toAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who recieves the AVAX - * @param fromAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who owns the AVAX + * @param fromSigner The addresses being used to send and verify the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} * @param changeAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who gets the change leftover of the AVAX * @param destinationChain Optional. A {@link https://github.com/feross/buffer|Buffer} for the chainid where to send the asset. * @param fee Optional. The amount of fees to burn in its smallest denomination, represented as {@link https://github.com/indutny/bn.js/|BN} @@ -331,7 +361,7 @@ export class Builder { amount: BN, amountAssetID: Buffer, toAddresses: Buffer[], - fromAddresses: Buffer[], + fromSigner: FromSigner, destinationChain: Buffer, changeAddresses: Buffer[] = undefined, fee: BN = zero, @@ -344,6 +374,7 @@ export class Builder { ): Promise => { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] + let owners: OutputOwners[] = [] if (typeof changeAddresses === "undefined") { changeAddresses = toAddresses @@ -364,9 +395,10 @@ export class Builder { } const aad: AssetAmountDestination = new AssetAmountDestination( - [], - 0, - fromAddresses, + toAddresses, + toThreshold, + fromSigner.from, + fromSigner.signer, changeAddresses, changeThreshold ) @@ -392,11 +424,12 @@ export class Builder { ins = aad.getInputs() outs = singleAsset ? aad.getAllOutputs() : aad.getChangeOutputs() exports = singleAsset ? [] : aad.getOutputs() + owners = aad.getOutputOwners() } else { throw minSpendableErr } - const exportTx: ExportTx = new ExportTx( + const baseTx: ExportTx = new ExportTx( networkID, blockchainID, outs, @@ -415,7 +448,8 @@ export class Builder { ] ) - return new UnsignedTx(exportTx) + baseTx.setOutputOwners(owners) + return new UnsignedTx(baseTx) } /** @@ -423,7 +457,7 @@ export class Builder { * * @param networkID Networkid, [[DefaultNetworkID]] * @param blockchainID Blockchainid, default undefined - * @param fromAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who pays the fees in AVAX + * @param fromSigner The addresses being used to send and verify the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} * @param changeAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who gets the change leftover from the fee payment * @param nodeID The node ID of the validator being added. * @param startTime The Unix time when the validator starts validating the Primary Network. @@ -433,7 +467,7 @@ export class Builder { * @param feeAssetID Optional. The assetID of the fees being burned. * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} - * @param subnetAuthCredentials Optional. An array of index and address to sign for each SubnetAuth. + * @param subnetAuth Optional. An Auth struct which contains the subnet Auth and the signers. * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned transaction created from the passed in parameters. @@ -441,7 +475,7 @@ export class Builder { buildAddSubnetValidatorTx = async ( networkID: number = DefaultNetworkID, blockchainID: Buffer, - fromAddresses: Buffer[], + fromSigner: FromSigner, changeAddresses: Buffer[], nodeID: Buffer, startTime: BN, @@ -452,12 +486,12 @@ export class Builder { feeAssetID: Buffer = undefined, memo: Buffer = undefined, asOf: BN = zero, - subnetAuthCredentials: [number, Buffer][] = [], - nodeCredentials: [number, Buffer] = undefined, + subnetAuth: Auth = { addresses: [], threshold: 0, signer: [] }, changeThreshold: number = 1 ): Promise => { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] + let owners: OutputOwners[] = [] const now: BN = UnixNow() if (startTime.lt(now) || endTime.lte(startTime)) { @@ -466,17 +500,12 @@ export class Builder { ) } - if (this.caminoEnabled && nodeCredentials == undefined) { - throw new Error( - "CaminoExecutor.buildAddSubnetValidatorTx -- nodeCredentials must be provided when Camino is enabled" - ) - } - if (this._feeCheck(fee, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( [], 0, - fromAddresses, + fromSigner.from, + fromSigner.signer, changeAddresses, changeThreshold ) @@ -491,12 +520,13 @@ export class Builder { if (typeof minSpendableErr === "undefined") { ins = aad.getInputs() outs = aad.getAllOutputs() + owners = aad.getOutputOwners() } else { throw minSpendableErr } } - const addSubnetValidatorTx: AddSubnetValidatorTx = new AddSubnetValidatorTx( + const baseTx: AddSubnetValidatorTx = new AddSubnetValidatorTx( networkID, blockchainID, outs, @@ -508,22 +538,22 @@ export class Builder { weight, subnetID ) - subnetAuthCredentials.forEach( - (subnetAuthCredential: [number, Buffer]): void => { - addSubnetValidatorTx.addSignatureIdx( - subnetAuthCredential[0], - subnetAuthCredential[1] - ) - } + subnetAuth.signer.forEach((subnetSigner): void => { + baseTx.addSignatureIdx(subnetSigner[0], subnetSigner[1]) + }) + + // We need to fetch the AUTH for later msig verification + // For now we use simply what we get in subnetAuth + owners.push( + new OutputOwners(subnetAuth.addresses, ZeroBN, subnetAuth.threshold) ) if (this.caminoEnabled) { - addSubnetValidatorTx.setNodeSignatureIdx( - nodeCredentials[0], - nodeCredentials[1] - ) + baseTx.includeNodeSignature() + owners.push(new OutputOwners([nodeID], ZeroBN, 1)) } - return new UnsignedTx(addSubnetValidatorTx) + baseTx.setOutputOwners(owners) + return new UnsignedTx(baseTx) } /** @@ -533,7 +563,7 @@ export class Builder { * @param blockchainID Blockchainid, default undefined * @param avaxAssetID {@link https://github.com/feross/buffer|Buffer} of the asset ID for AVAX * @param toAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} recieves the stake at the end of the staking period - * @param fromAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who pays the fees and the stake + * @param fromSigner The addresses being used to send and verify the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} * @param changeAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who gets the change leftover from the staking payment * @param nodeID The node ID of the validator being added. * @param startTime The Unix time when the validator starts validating the Primary Network. @@ -556,7 +586,7 @@ export class Builder { blockchainID: Buffer, avaxAssetID: Buffer, toAddresses: Buffer[], - fromAddresses: Buffer[], + fromSigner: FromSigner, changeAddresses: Buffer[], nodeID: Buffer, startTime: BN, @@ -592,6 +622,7 @@ export class Builder { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] let stakeOuts: TransferableOutput[] = [] + let owners: OutputOwners[] = [] const now: BN = UnixNow() if (startTime.lt(now) || endTime.lte(startTime)) { @@ -603,7 +634,8 @@ export class Builder { const aad: AssetAmountDestination = new AssetAmountDestination( toAddresses, toThreshold, - fromAddresses, + fromSigner.from, + fromSigner.signer, changeAddresses, changeThreshold ) @@ -626,6 +658,7 @@ export class Builder { ins = aad.getInputs() outs = aad.getChangeOutputs() stakeOuts = aad.getOutputs() + owners = aad.getOutputOwners() } else { throw minSpendableErr } @@ -636,7 +669,7 @@ export class Builder { rewardThreshold ) - const UTx: AddDelegatorTx = new AddDelegatorTx( + const baseTx: AddDelegatorTx = new AddDelegatorTx( networkID, blockchainID, outs, @@ -649,7 +682,8 @@ export class Builder { stakeOuts, new ParseableOutput(rewardOutputOwners) ) - return new UnsignedTx(UTx) + baseTx.setOutputOwners(owners) + return new UnsignedTx(baseTx) } /** @@ -659,7 +693,7 @@ export class Builder { * @param blockchainID BlockchainID, default undefined * @param avaxAssetID {@link https://github.com/feross/buffer|Buffer} of the asset ID for AVAX * @param toAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} recieves the stake at the end of the staking period - * @param fromAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who pays the fees and the stake + * @param fromSigner The addresses being used to send and verify the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} * @param changeAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who gets the change leftover from the staking payment * @param nodeID The node ID of the validator being added. * @param startTime The Unix time when the validator starts validating the Primary Network. @@ -681,7 +715,7 @@ export class Builder { networkID: number = DefaultNetworkID, blockchainID: Buffer, toAddresses: Buffer[], - fromAddresses: Buffer[], + fromSigner: FromSigner, changeAddresses: Buffer[], nodeID: Buffer, startTime: BN, @@ -704,7 +738,7 @@ export class Builder { networkID, blockchainID, toAddresses, - fromAddresses, + fromSigner, changeAddresses, nodeID, startTime, @@ -724,6 +758,7 @@ export class Builder { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] let stakeOuts: TransferableOutput[] = [] + let owners: OutputOwners[] = [] const now: BN = UnixNow() if (startTime.lt(now) || endTime.lte(startTime)) { @@ -741,7 +776,8 @@ export class Builder { const aad: AssetAmountDestination = new AssetAmountDestination( toAddresses, toThreshold, - fromAddresses, + fromSigner.from, + fromSigner.signer, changeAddresses, changeThreshold ) @@ -764,6 +800,7 @@ export class Builder { ins = aad.getInputs() outs = aad.getChangeOutputs() stakeOuts = aad.getOutputs() + owners = aad.getOutputOwners() } else { throw minSpendableErr } @@ -774,7 +811,7 @@ export class Builder { rewardThreshold ) - const UTx: AddValidatorTx = new AddValidatorTx( + const baseTx: AddValidatorTx = new AddValidatorTx( networkID, blockchainID, outs, @@ -788,7 +825,8 @@ export class Builder { new ParseableOutput(rewardOutputOwners), delegationFee ) - return new UnsignedTx(UTx) + baseTx.setOutputOwners(owners) + return new UnsignedTx(baseTx) } /** @@ -796,7 +834,7 @@ export class Builder { * * @param networkID Networkid, [[DefaultNetworkID]] * @param blockchainID Blockchainid, default undefined - * @param fromAddresses The addresses being used to send the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} + * @param fromSigner The addresses being used to send and verify the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} * @param changeAddresses The addresses that can spend the change remaining from the spent UTXOs. * @param subnetOwnerAddresses An array of {@link https://github.com/feross/buffer|Buffer} for the addresses to add to a subnet * @param subnetOwnerThreshold The number of owners's signatures required to add a validator to the network @@ -811,7 +849,7 @@ export class Builder { buildCreateSubnetTx = async ( networkID: number = DefaultNetworkID, blockchainID: Buffer, - fromAddresses: Buffer[], + fromSigner: FromSigner, changeAddresses: Buffer[], subnetOwnerAddresses: Buffer[], subnetOwnerThreshold: number, @@ -823,12 +861,14 @@ export class Builder { ): Promise => { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] + let owners: OutputOwners[] = [] if (this._feeCheck(fee, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( [], 0, - fromAddresses, + fromSigner.from, + fromSigner.signer, changeAddresses, changeThreshold ) @@ -843,6 +883,7 @@ export class Builder { if (typeof minSpendableErr === "undefined") { ins = aad.getInputs() outs = aad.getAllOutputs() + owners = aad.getOutputOwners() } else { throw minSpendableErr } @@ -854,7 +895,7 @@ export class Builder { locktime, subnetOwnerThreshold ) - const createSubnetTx: CreateSubnetTx = new CreateSubnetTx( + const baseTx: CreateSubnetTx = new CreateSubnetTx( networkID, blockchainID, outs, @@ -862,8 +903,8 @@ export class Builder { memo, subnetOwners ) - - return new UnsignedTx(createSubnetTx) + baseTx.setOutputOwners(owners) + return new UnsignedTx(baseTx) } /** @@ -871,7 +912,7 @@ export class Builder { * * @param networkID Networkid, [[DefaultNetworkID]] * @param blockchainID Blockchainid, default undefined - * @param fromAddresses The addresses being used to send the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} + * @param fromSigner The addresses being used to send and verify the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} * @param changeAddresses The addresses that can spend the change remaining from the spent UTXOs. * @param subnetID Optional ID of the Subnet that validates this blockchain * @param chainName Optional A human readable name for the chain; need not be unique @@ -882,7 +923,7 @@ export class Builder { * @param feeAssetID Optional. The assetID of the fees being burned * @param memo Optional contains arbitrary bytes, up to 256 bytes * @param asOf Optional. The timestamp to verify the transaction against as a {@link https://github.com/indutny/bn.js/|BN} - * @param subnetAuthCredentials Optional. An array of index and address to sign for each SubnetAuth. + * @param subnetAuth Optional. An Auth struct to sign for the Subnet. * @param changeThreshold Optional. The number of signatures required to spend the funds in the resultant change UTXO * * @returns An unsigned CreateChainTx created from the passed in parameters. @@ -890,7 +931,7 @@ export class Builder { buildCreateChainTx = async ( networkID: number = DefaultNetworkID, blockchainID: Buffer, - fromAddresses: Buffer[], + fromSigner: FromSigner, changeAddresses: Buffer[], subnetID: string | Buffer = undefined, chainName: string = undefined, @@ -901,17 +942,19 @@ export class Builder { feeAssetID: Buffer = undefined, memo: Buffer = undefined, asOf: BN = zero, - subnetAuthCredentials: [number, Buffer][] = [], + subnetAuth: Auth = { addresses: [], threshold: 0, signer: [] }, changeThreshold: number = 1 ): Promise => { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] + let owners: OutputOwners[] = [] if (this._feeCheck(fee, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( [], 0, - fromAddresses, + fromSigner.from, + fromSigner.signer, changeAddresses, changeThreshold ) @@ -926,12 +969,13 @@ export class Builder { if (typeof minSpendableErr === "undefined") { ins = aad.getInputs() outs = aad.getAllOutputs() + owners = aad.getOutputOwners() } else { throw minSpendableErr } } - const createChainTx: CreateChainTx = new CreateChainTx( + const baseTx: CreateChainTx = new CreateChainTx( networkID, blockchainID, outs, @@ -943,16 +987,18 @@ export class Builder { fxIDs, genesisData ) - subnetAuthCredentials.forEach( - (subnetAuthCredential: [number, Buffer]): void => { - createChainTx.addSignatureIdx( - subnetAuthCredential[0], - subnetAuthCredential[1] - ) - } + subnetAuth.signer.forEach((subnetAuthSigner): void => { + baseTx.addSignatureIdx(subnetAuthSigner[0], subnetAuthSigner[1]) + }) + + // We need to fetch the AUTH for later msig verification + // For now we use simply what we get in subnetAuth + owners.push( + new OutputOwners(subnetAuth.addresses, ZeroBN, subnetAuth.threshold) ) - return new UnsignedTx(createChainTx) + baseTx.setOutputOwners(owners) + return new UnsignedTx(baseTx) } /** @@ -962,7 +1008,7 @@ export class Builder { * @param networkID Networkid, [[DefaultNetworkID]] * @param blockchainID Blockchainid, default undefined * @param toAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who received the staked tokens at the end of the staking period - * @param fromAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who own the staking UTXOs the fees in AVAX + * @param fromSigner The addresses being used to send and verify the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} * @param changeAddresses An array of addresses as {@link https://github.com/feross/buffer|Buffer} who gets the change leftover from the fee payment * @param nodeID The node ID of the validator being added. * @param startTime The Unix time when the validator starts validating the Primary Network. @@ -982,7 +1028,7 @@ export class Builder { networkID: number = DefaultNetworkID, blockchainID: Buffer, to: Buffer[], - from: Buffer[], + fromSigner: FromSigner, change: Buffer[], nodeID: Buffer, startTime: BN, @@ -999,6 +1045,7 @@ export class Builder { ): Promise => { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] + let owners: OutputOwners[] = [] const now: BN = UnixNow() if (startTime.lt(now) || endTime.lte(startTime)) { @@ -1010,7 +1057,8 @@ export class Builder { const aad: AssetAmountDestination = new AssetAmountDestination( to, toThreshold, - from, + fromSigner.from, + fromSigner.signer, change, changeThreshold ) @@ -1026,6 +1074,7 @@ export class Builder { if (typeof minSpendableErr === "undefined") { ins = aad.getInputs() outs = aad.getAllOutputs() + owners = aad.getOutputOwners() } else { throw minSpendableErr } @@ -1036,7 +1085,7 @@ export class Builder { rewardThreshold ) - const tx: CaminoAddValidatorTx = new CaminoAddValidatorTx( + const baseTx: CaminoAddValidatorTx = new CaminoAddValidatorTx( networkID, blockchainID, outs, @@ -1049,7 +1098,8 @@ export class Builder { new ParseableOutput(rewardOutputOwners) ) - return new UnsignedTx(tx) + baseTx.setOutputOwners(owners) + return new UnsignedTx(baseTx) } /** @@ -1057,7 +1107,7 @@ export class Builder { * * @param networkID Networkid, [[DefaultNetworkID]] * @param blockchainID Blockchainid, default undefined - * @param fromAddresses The addresses being used to send the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} + * @param fromSigner The addresses being used to send and verify the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} * @param changeAddresses The addresses that can spend the change remaining from the spent UTXOs. * @param address The address to alter state. * @param state The state to set or remove on the given address @@ -1073,7 +1123,7 @@ export class Builder { buildAddressStateTx = async ( networkID: number = DefaultNetworkID, blockchainID: Buffer, - fromAddresses: Buffer[], + fromSigner: FromSigner, changeAddresses: Buffer[], address: Buffer, state: number, @@ -1086,12 +1136,14 @@ export class Builder { ): Promise => { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] + let owners: OutputOwners[] = [] if (this._feeCheck(fee, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( [], 0, - fromAddresses, + fromSigner.from, + fromSigner.signer, changeAddresses, changeThreshold ) @@ -1112,7 +1164,7 @@ export class Builder { } } - const addressStateTx: AddressStateTx = new AddressStateTx( + const baseTx: AddressStateTx = new AddressStateTx( networkID, blockchainID, outs, @@ -1123,7 +1175,8 @@ export class Builder { remove ) - return new UnsignedTx(addressStateTx) + baseTx.setOutputOwners(owners) + return new UnsignedTx(baseTx) } /** @@ -1131,12 +1184,12 @@ export class Builder { * * @param networkID Networkid, [[DefaultNetworkID]] * @param blockchainID Blockchainid, default undefined - * @param fromAddresses The addresses being used to send the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} + * @param fromSigner The addresses being used to send and verify the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} * @param changeAddresses The addresses that can spend the change remaining from the spent UTXOs. * @param oldNodeID Optional. ID of the existing NodeID to replace or remove. * @param newNodeID Optional. ID of the newNodID to register address. * @param address The consortiumMemberAddress, single or multi-sig. - * @param consortiumMemberAuthCredentials An array of index and address to sign for each SubnetAuth. + * @param addressAuths An array of index and address to verify ownership of address. * @param fee Optional. The amount of fees to burn in its smallest denomination, represented as {@link https://github.com/indutny/bn.js/|BN} * @param feeAssetID Optional. The assetID of the fees being burned * @param memo Optional contains arbitrary bytes, up to 256 bytes @@ -1148,12 +1201,12 @@ export class Builder { buildRegisterNodeTx = async ( networkID: number = DefaultNetworkID, blockchainID: Buffer, - fromAddresses: Buffer[], + fromSigner: FromSigner, changeAddresses: Buffer[], - oldNodeID: string | Buffer = undefined, - newNodeID: string | Buffer = undefined, + oldNodeID: Buffer = undefined, + newNodeID: Buffer = undefined, address: Buffer = undefined, - consortiumMemberAuthCredentials: [number, Buffer][] = [], + addressAuths: [number, Buffer][] = [], fee: BN = zero, feeAssetID: Buffer = undefined, memo: Buffer = undefined, @@ -1162,12 +1215,14 @@ export class Builder { ): Promise => { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] + let owners: OutputOwners[] = [] if (this._feeCheck(fee, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( [], 0, - fromAddresses, + fromSigner.from, + fromSigner.signer, changeAddresses, changeThreshold ) @@ -1183,12 +1238,13 @@ export class Builder { if (typeof minSpendableErr === "undefined") { ins = aad.getInputs() outs = aad.getAllOutputs() + owners = aad.getOutputOwners() } else { throw minSpendableErr } } - const registerNodeTx: RegisterNodeTx = new RegisterNodeTx( + const baseTx: RegisterNodeTx = new RegisterNodeTx( networkID, blockchainID, outs, @@ -1198,16 +1254,20 @@ export class Builder { newNodeID, address ) - consortiumMemberAuthCredentials.forEach( - (subnetAuthCredential: [number, Buffer]): void => { - registerNodeTx.addSignatureIdx( - subnetAuthCredential[0], - subnetAuthCredential[1] - ) - } + + addressAuths.forEach((addressAuth) => { + baseTx.addSignatureIdx(addressAuth[0], addressAuth[1]) + }) + + owners.push( + newNodeID && !oldNodeID + ? new OutputOwners([newNodeID], ZeroBN, 1) + : new OutputOwners() ) + owners.push(new OutputOwners([address], ZeroBN, 1)) - return new UnsignedTx(registerNodeTx) + baseTx.setOutputOwners(owners) + return new UnsignedTx(baseTx) } /** @@ -1215,7 +1275,7 @@ export class Builder { * * @param networkID Networkid, [[DefaultNetworkID]] * @param blockchainID Blockchainid, default undefined - * @param fromAddresses The addresses being used to send the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} + * @param fromSigner The addresses being used to send and verify the funds from the UTXOs {@link https://github.com/feross/buffer|Buffer} * @param changeAddresses The addresses that can spend the change remaining from the spent UTXOs. * @param depositOfferID ID of the deposit offer. * @param depositDuration Duration of the deposit @@ -1231,7 +1291,7 @@ export class Builder { buildDepositTx = async ( networkID: number = DefaultNetworkID, blockchainID: Buffer, - fromAddresses: Buffer[], + fromSigner: FromSigner, changeAddresses: Buffer[], depositOfferID: string | Buffer, depositDuration: number | Buffer, @@ -1244,12 +1304,14 @@ export class Builder { ): Promise => { let ins: TransferableInput[] = [] let outs: TransferableOutput[] = [] + let owners: OutputOwners[] = [] if (this._feeCheck(fee, feeAssetID)) { const aad: AssetAmountDestination = new AssetAmountDestination( [], 0, - fromAddresses, + fromSigner.from, + fromSigner.signer, changeAddresses, changeThreshold ) @@ -1265,6 +1327,7 @@ export class Builder { if (typeof minSpendableErr === "undefined") { ins = aad.getInputs() outs = aad.getAllOutputs() + owners = aad.getOutputOwners() } else { throw minSpendableErr } @@ -1276,7 +1339,7 @@ export class Builder { rewardsOwner.getThreshold() ) - const depositTx: DepositTx = new DepositTx( + const baseTx: DepositTx = new DepositTx( networkID, blockchainID, outs, @@ -1287,7 +1350,8 @@ export class Builder { new ParseableOutput(secpOwners) ) - return new UnsignedTx(depositTx) + baseTx.setOutputOwners(owners) + return new UnsignedTx(baseTx) } _feeCheck(fee: BN, feeAssetID: Buffer): boolean { diff --git a/src/apis/platformvm/constants.ts b/src/apis/platformvm/constants.ts index f820bea41..b0df39dd5 100644 --- a/src/apis/platformvm/constants.ts +++ b/src/apis/platformvm/constants.ts @@ -55,6 +55,12 @@ export class PlatformVMConstants { static DEPOSITTX: number = PlatformVMConstants.CUSTOM_TYPE_ID + 5 static UNLOCKDEPOSITTX: number = PlatformVMConstants.CUSTOM_TYPE_ID + 6 static REGISTERNODETX: number = PlatformVMConstants.CUSTOM_TYPE_ID + 7 + // static BASETX: number = PlatformVMConstants.CUSTOM_TYPE_ID + 8 + static MULTISIGALIASTX: number = PlatformVMConstants.CUSTOM_TYPE_ID + 9 + static CLAIMTX: number = PlatformVMConstants.CUSTOM_TYPE_ID + 10 + static REWARDSIMPORTTX: number = PlatformVMConstants.CUSTOM_TYPE_ID + 11 + static SECPMULTISIGCREDENTIAL: number = + PlatformVMConstants.CUSTOM_TYPE_ID + 12 // Length Constants static ASSETIDLEN: number = 32 diff --git a/src/apis/platformvm/createchaintx.ts b/src/apis/platformvm/createchaintx.ts index 17fe78570..cb36b7d1b 100644 --- a/src/apis/platformvm/createchaintx.ts +++ b/src/apis/platformvm/createchaintx.ts @@ -7,7 +7,7 @@ import BinTools from "../../utils/bintools" import { PlatformVMConstants } from "./constants" import { TransferableOutput } from "./outputs" import { TransferableInput } from "./inputs" -import { Credential, MultisigAliasSet, SigIdx, Signature } from "../../common" +import { Credential, SigIdx, Signature } from "../../common" import { BaseTx } from "./basetx" import { DefaultNetworkID } from "../../utils/constants" import { Serialization, SerializedEncoding } from "../../utils/serialization" @@ -287,11 +287,6 @@ export class CreateChainTx extends BaseTx { return creds } - resolveMultisigIndices(resolver: MultisigAliasSet) { - super.resolveMultisigIndices(resolver) - this.setSigIdxs(resolver.resolveMultisig(this.sigIdxs)) - } - /** * Class representing an unsigned CreateChain transaction. * diff --git a/src/apis/platformvm/credentials.ts b/src/apis/platformvm/credentials.ts index 5b5e3630d..3dd8e8ad5 100644 --- a/src/apis/platformvm/credentials.ts +++ b/src/apis/platformvm/credentials.ts @@ -4,7 +4,7 @@ */ import { PlatformVMConstants } from "./constants" -import { Credential } from "../../common/credentials" +import { Credential, SECPMultisigCredential } from "../../common" import { CredIdError } from "../../utils/errors" /** @@ -18,11 +18,15 @@ export const SelectCredentialClass = ( credid: number, ...args: any[] ): Credential => { - if (credid === PlatformVMConstants.SECPCREDENTIAL) { - return new SECPCredential(...args) + switch (credid) { + case PlatformVMConstants.SECPCREDENTIAL: + return new SECPCredential(...args) + case PlatformVMConstants.SECPMULTISIGCREDENTIAL: + return new SECPMultisigCredential(credid) + default: + /* istanbul ignore next */ + throw new CredIdError("Error - SelectCredentialClass: unknown credid") } - /* istanbul ignore next */ - throw new CredIdError("Error - SelectCredentialClass: unknown credid") } export class SECPCredential extends Credential { @@ -31,10 +35,6 @@ export class SECPCredential extends Credential { //serialize and deserialize both are inherited - getCredentialID(): number { - return this._typeID - } - clone(): this { let newbase: SECPCredential = new SECPCredential() newbase.fromBuffer(this.toBuffer()) diff --git a/src/apis/platformvm/importtx.ts b/src/apis/platformvm/importtx.ts index 777f29a3d..41aaabf3b 100644 --- a/src/apis/platformvm/importtx.ts +++ b/src/apis/platformvm/importtx.ts @@ -9,7 +9,7 @@ import { TransferableOutput } from "./outputs" import { TransferableInput } from "./inputs" import { KeyChain, KeyPair } from "./keychain" import { SelectCredentialClass } from "./credentials" -import { Signature, MultisigAliasSet, SigIdx, Credential } from "../../common" +import { Signature, SigIdx, Credential, OutputOwners } from "../../common" import { BaseTx } from "./basetx" import { DefaultNetworkID } from "../../utils/constants" import { Serialization, SerializedEncoding } from "../../utils/serialization" @@ -152,13 +152,6 @@ export class ImportTx extends BaseTx { return creds } - resolveMultisigIndices(resolver: MultisigAliasSet) { - for (let i: number = 0; i < this.importIns.length; i++) { - const input = this.importIns[`${i}`].getInput() - input.setSigIdxs(resolver.resolveMultisig(input.getSigIdxs())) - } - } - clone(): this { let newbase: ImportTx = new ImportTx() newbase.fromBuffer(this.toBuffer()) diff --git a/src/apis/platformvm/interfaces.ts b/src/apis/platformvm/interfaces.ts index 672e88265..986257157 100644 --- a/src/apis/platformvm/interfaces.ts +++ b/src/apis/platformvm/interfaces.ts @@ -6,7 +6,8 @@ import BN from "bn.js" import { PersistanceOptions } from "../../utils/persistenceoptions" import { TransferableInput, TransferableOutput } from "." -import { UTXOSet } from "../platformvm/utxos" +import { UTXOSet } from "./utxos" +import { OutputOwners } from "../../common/output" export interface AddressParams { address: string @@ -310,6 +311,7 @@ export interface MultisigAliasReply extends Owner { export interface SpendParams { from: string[] | string + signer: string[] | string to?: Owner change?: Owner @@ -323,4 +325,5 @@ export interface SpendParams { export interface SpendReply { ins: TransferableInput[] out: TransferableOutput[] + owners: OutputOwners[] } diff --git a/src/apis/platformvm/registernodetx.ts b/src/apis/platformvm/registernodetx.ts index d427ebf53..e3dec42a5 100644 --- a/src/apis/platformvm/registernodetx.ts +++ b/src/apis/platformvm/registernodetx.ts @@ -7,7 +7,7 @@ import BinTools from "../../utils/bintools" import { PlatformVMConstants } from "./constants" import { TransferableOutput } from "./outputs" import { TransferableInput } from "./inputs" -import { Credential, MultisigAliasSet, SigIdx, Signature } from "../../common" +import { Credential, SigIdx, Signature } from "../../common" import { BaseTx } from "./basetx" import { DefaultNetworkID } from "../../utils/constants" import { @@ -221,11 +221,6 @@ export class RegisterNodeTx extends BaseTx { return creds } - resolveMultisigIndices(resolver: MultisigAliasSet) { - super.resolveMultisigIndices(resolver) - this.setSigIdxs(resolver.resolveMultisig(this.sigIdxs)) - } - /** * Class representing an unsigned RegisterNode transaction. * @@ -244,25 +239,16 @@ export class RegisterNodeTx extends BaseTx { outs: TransferableOutput[] = undefined, ins: TransferableInput[] = undefined, memo: Buffer = undefined, - oldNodeID: string | Buffer = undefined, - newNodeID: string | Buffer = undefined, + oldNodeID: Buffer = undefined, + newNodeID: Buffer = undefined, address: Buffer = undefined ) { super(networkID, blockchainID, outs, ins, memo) - if (typeof oldNodeID != "undefined") { - if (typeof oldNodeID === "string") { - this.oldNodeID = NodeIDStringToBuffer(oldNodeID) - } else { - this.oldNodeID = oldNodeID - } - } - if (typeof newNodeID != "undefined") { - if (typeof newNodeID === "string") { - this.newNodeID = NodeIDStringToBuffer(newNodeID) - } else { - this.newNodeID = newNodeID - } - } + + if (typeof oldNodeID !== "undefined") this.oldNodeID = oldNodeID + + if (typeof newNodeID !== "undefined") this.newNodeID = newNodeID + if (typeof address != "undefined") { this.consortiumMemberAddress = address } diff --git a/src/apis/platformvm/spender.ts b/src/apis/platformvm/spender.ts index e0b4494ce..c5a8700b2 100644 --- a/src/apis/platformvm/spender.ts +++ b/src/apis/platformvm/spender.ts @@ -31,6 +31,10 @@ export class Spender { .getSenders() .map((a) => this.platformAPI.addressFromBuffer(a)) + const signer = aad + .getSigners() + .map((a) => this.platformAPI.addressFromBuffer(a)) + const to = aad .getDestinations() .map((a) => this.platformAPI.addressFromBuffer(a)) @@ -43,6 +47,7 @@ export class Spender { const result = await this.platformAPI.spend( addr, + signer, to, aad.getDestinationsThreshold(), lockTime, @@ -60,7 +65,7 @@ export class Spender { result.out.forEach((out) => { aad.addOutput(out) }) - + aad.setOutputOwners(result.owners) return } } diff --git a/src/apis/platformvm/tx.ts b/src/apis/platformvm/tx.ts index 0265b5ac2..4bc1ae30e 100644 --- a/src/apis/platformvm/tx.ts +++ b/src/apis/platformvm/tx.ts @@ -7,6 +7,7 @@ import BinTools from "../../utils/bintools" import { PlatformVMConstants } from "./constants" import { SelectCredentialClass } from "./credentials" import { + MultisigKeyChain, SignerKeyChain, SignerKeyPair, StandardTx, @@ -25,6 +26,9 @@ import { } from "./validationtx" import { CreateSubnetTx } from "./createsubnettx" import { TransactionError } from "../../utils/errors" +import { RegisterNodeTx } from "./registernodetx" +import { DepositTx } from "./depositTx" +import { AddressStateTx } from "./addressstatetx" /** * @ignore @@ -53,6 +57,12 @@ export const SelectTxClass = (txtype: number, ...args: any[]): BaseTx => { return new CaminoAddValidatorTx(...args) } else if (txtype === PlatformVMConstants.CREATESUBNETTX) { return new CreateSubnetTx(...args) + } else if (txtype === PlatformVMConstants.REGISTERNODETX) { + return new RegisterNodeTx(...args) + } else if (txtype === PlatformVMConstants.DEPOSITTX) { + return new DepositTx(...args) + } else if (txtype === PlatformVMConstants.ADDRESSSTATETX) { + return new AddressStateTx(...args) } /* istanbul ignore next */ throw new TransactionError("Error - SelectTxClass: unknown txtype") @@ -101,7 +111,11 @@ export class UnsignedTx extends StandardUnsignedTx< const msg: Buffer = Buffer.from( createHash("sha256").update(txbuff).digest() ) - const creds: Credential[] = this.transaction.sign(msg, kc) + + const creds: Credential[] = + kc instanceof MultisigKeyChain + ? kc.getCredentials() + : this.transaction.sign(msg, kc) return new Tx(this, creds) } } diff --git a/src/apis/platformvm/utxos.ts b/src/apis/platformvm/utxos.ts index aec0c47a5..843a7ff46 100644 --- a/src/apis/platformvm/utxos.ts +++ b/src/apis/platformvm/utxos.ts @@ -28,7 +28,7 @@ import { AssetAmount } from "../../common/assetamount" import { BaseInput } from "../../common/input" -import { BaseOutput } from "../../common/output" +import { BaseOutput, OutputOwners } from "../../common/output" import { Serialization, SerializedEncoding } from "../../utils/serialization" import { UTXOError, @@ -123,7 +123,33 @@ export class UTXO extends StandardUTXO { export class AssetAmountDestination extends StandardAssetAmountDestination< TransferableOutput, TransferableInput -> {} +> { + protected signers: Buffer[] + protected outputOwners: OutputOwners[] = [] + + getSigners = (): Buffer[] => this.signers + + setOutputOwners = (owners: OutputOwners[]) => (this.outputOwners = owners) + getOutputOwners = (): OutputOwners[] => this.outputOwners + + constructor( + destinations: Buffer[], + destinationsThreshold: number, + senders: Buffer[], + signers: Buffer[], + changeAddresses: Buffer[], + changeAddressesThreshold: number + ) { + super( + destinations, + destinationsThreshold, + senders, + changeAddresses, + changeAddressesThreshold + ) + this.signers = signers + } +} /** * Class representing a set of [[UTXO]]s. diff --git a/src/common/credentials.ts b/src/common/credentials.ts index 108cf213c..95e67e4ab 100644 --- a/src/common/credentials.ts +++ b/src/common/credentials.ts @@ -127,7 +127,9 @@ export abstract class Credential extends Serializable { protected sigArray: Signature[] = [] - abstract getCredentialID(): number + getCredentialID(): number { + return this._typeID + } /** * Set the codecID @@ -183,3 +185,71 @@ export abstract class Credential extends Serializable { } } } + +export class SECPMultisigCredential extends Credential { + protected _typeName = "SECPMultisigCredential" + protected _typeID = undefined + + protected sigIdxs: SigIdx[] = [] + + /** + * Adds a SignatureIndex to the credentials. + */ + addSSignatureIndex = (sigIdx: SigIdx): void => { + this.sigIdxs.push(sigIdx) + } + + clone(): this { + const newbase = new SECPMultisigCredential(this._typeID) + newbase.fromBuffer(this.toBuffer()) + return newbase as this + } + + create(...args: any[]): this { + return new SECPMultisigCredential( + args.length == 1 ? args[0] : this._typeID + ) as this + } + + select(id: number, ...args: any[]): Credential { + if (id === this._typeID) return this.create(args) + } + + fromBuffer(bytes: Buffer, offset: number = 0): number { + offset = super.fromBuffer(bytes, offset) + const sigIdxlen: number = bintools + .copyFrom(bytes, offset, offset + 4) + .readUInt32BE(0) + offset += 4 + this.sigIdxs = [] + for (let i = 0; i < sigIdxlen; i++) { + const sigIdx: SigIdx = new SigIdx() + offset = sigIdx.fromBuffer(bytes, offset) + this.sigIdxs.push(sigIdx) + } + return offset + } + + toBuffer(): Buffer { + // The signatures + const superBuff: Buffer = super.toBuffer() + + const sigIdxlen: Buffer = Buffer.alloc(4) + sigIdxlen.writeInt32BE(this.sigIdxs.length, 0) + const barr: Buffer[] = [superBuff, sigIdxlen] + let bsize: number = superBuff.length + sigIdxlen.length + + for (const sigIdx of this.sigIdxs) { + const sigIdxBuff: Buffer = sigIdx.toBuffer() + bsize += sigIdxBuff.length + barr.push(sigIdxBuff) + } + return Buffer.concat(barr, bsize) + } + + constructor(typeID: number, sigIdxs?: SigIdx[], sigarray?: Signature[]) { + super(sigarray) + this._typeID = typeID + if (sigIdxs) this.sigIdxs = sigIdxs + } +} diff --git a/src/common/multisigkeychain.ts b/src/common/multisigkeychain.ts index 94305588f..c8998e90e 100644 --- a/src/common/multisigkeychain.ts +++ b/src/common/multisigkeychain.ts @@ -4,7 +4,13 @@ */ import { Buffer } from "buffer/" -import { OutputOwners, SigIdx } from "." +import { + Credential, + OutputOwners, + SECPMultisigCredential, + SigIdx, + Signature +} from "." import { Serialization, SerializedType } from "../utils" import BinTools from "../utils/bintools" @@ -18,113 +24,6 @@ const TooManySignatures = new SignatureError("too many signatures") const serialization: Serialization = Serialization.getInstance() const bintools: BinTools = BinTools.getInstance() const MaxSignatures = 256 -const WildcardBuffer = new Buffer([0xff, 0xff, 0xff, 0xff]) - -export class MultisigAliasSet { - protected msigs: Map - protected addresses: Set - protected isDryRun = false - - resolveMultisig(source: SigIdx[]): SigIdx[] { - type stackItem = { - index: number - verified: number - owners: OutputOwners - } - - var visited: number = 0 - const cycleCheck = new Set() - const stack: stackItem[] = [ - { - index: 0, - verified: 0, - owners: new OutputOwners( - source.map((s) => s.getSource()), - undefined, - source.length - ) - } - ] - - const result: SigIdx[] = [] - const helper = Buffer.alloc(4) - const noAddresses = this.addresses.size === 0 - - Stack: while (stack.length > 0) { - // get head - const currentStack = stack[stack.length - 1] - while ( - currentStack.index < currentStack.owners.getAddresses().length && - currentStack.verified < currentStack.owners.getThreshold() - ) { - // get the next address to check - const addr = currentStack.owners.getAddress(currentStack.index) - const addrString = addr.toString("hex") - currentStack.index++ - // Is it a multi-sig address ? - const alias = this.msigs.get(addrString) - if (alias !== undefined) { - // multi-sig - if (stack.length > MaxSignatures) { - throw TooManySignatures - } - if (cycleCheck.has(addrString)) { - throw new Error("cyclink multisig alias") - } - cycleCheck.add(addrString) - // Ignore empty alias definitions - if (alias.getThreshold() > 0) { - stack.push({ index: 0, verified: 0, owners: alias }) - continue Stack - } - } else { - // non-multi-sig - if (visited > MaxSignatures) { - throw TooManySignatures - } - // Special case for preparing Signavault - if (noAddresses || this.addresses.has(addrString)) { - const sigIdx = new SigIdx() - if (noAddresses) { - sigIdx.fromBuffer(WildcardBuffer) - } else { - sigIdx.setSource(addr) - helper.writeUIntBE(visited, 0, 4) - sigIdx.fromBuffer(helper) - } - result.push(sigIdx) - currentStack.verified++ - } - visited++ - } - } - // verify current level - if (currentStack.verified < currentStack.owners.getThreshold()) { - throw new SignatureError("not enough signatures") - } - // remove head - stack.pop() - // apply child verification - if (stack.length > 0) { - stack[stack.length - 1].verified++ - } - } - return this.isDryRun ? source : result - } - - dryRun(enable: boolean) { - this.isDryRun = enable - } - - clearAddresses(): void { - this.addresses.clear() - } - - constructor(msigs: Map, addresses: Set) { - this.msigs = msigs - this.addresses = addresses - } -} /** * Class for representing a generic multi signature key. @@ -207,6 +106,14 @@ export class MultisigKeyChain extends StandardKeyChain { protected chainID: string // The bytes which are signed by this txID protected signedBytes: Buffer + // the OutputOwners of all inputs and Auths inside the message + protected txOwners: OutputOwners[] + // the multisig aliases which take part in evaluation + protected msigAliases: Map + // Credentials for all the txOwners + protected sigIdxs: SigIdx[][] + // The CredentialID used for SECPMultisigCredential + protected credTypeID: number getHRP(): string { return this.hrp @@ -217,25 +124,31 @@ export class MultisigKeyChain extends StandardKeyChain { } create(...args: any[]): this { - if (args.length == 3) { - return new MultisigKeyChain(args[0], args[1], args[2]) as this + if (args.length == 4) { + return new MultisigKeyChain(args[0], args[1], args[2], args[4]) as this } return new MultisigKeyChain( - this.signedBytes, this.hrp, - this.chainID + this.chainID, + this.signedBytes, + this.credTypeID ) as this } clone(): this { const newkc = new MultisigKeyChain( - this.signedBytes, this.hrp, - this.chainID + this.chainID, + this.signedBytes, + this.credTypeID ) as this for (let k in this.keys) { newkc.addKey(this.keys[`${k}`].clone()) } + newkc.txOwners = new Array(this.txOwners.length) + this.txOwners.forEach((txo, index) => + newkc.txOwners[index].fromBuffer(txo.toBuffer()) + ) return newkc } @@ -253,10 +166,141 @@ export class MultisigKeyChain extends StandardKeyChain { return newkc } - constructor(signedBytes: Buffer, hrp: string, chainID: string) { + // Visit every txOutputOwner and try to verify with keys. + // Traverse into msig aliases. Throw if one cannot be fulfilled + buildSignatureIndices() { + this.sigIdxs = [] + for (const o of this.txOwners) this._traverseOwner(o) + } + + getCredentials(): Credential[] { + const result: SECPMultisigCredential[] = [] + for (const sigSet of this.sigIdxs) { + const cred = new SECPMultisigCredential(this.credTypeID) + for (const sigIdx of sigSet) { + cred.addSSignatureIndex(sigIdx) + const sig = new Signature() + sig.fromBuffer(this.getKey(sigIdx.getSource()).getPrivateKey()) + cred.addSignature(sig) + } + result.push(cred) + } + return result + } + + protected _traverseOwner(owner: OutputOwners): void { + var addrVisited: number = 0 + var addrVerified: number = 0 + + type stackItem = { + index: number + verified: number + addrVerifiedTotal: number + parentVerified: boolean + owners: OutputOwners + } + + const cycleCheck = new Set() + const stack: stackItem[] = [ + { + index: 0, + verified: 0, + addrVerifiedTotal: 0, + parentVerified: false, + owners: owner + } + ] + + const sigIdxs: SigIdx[] = [] + const helper = Buffer.alloc(4) + + Stack: while (stack.length > 0) { + // get head + var currentStack = stack[stack.length - 1] + while (currentStack.index < currentStack.owners.getAddressesLength()) { + // get the next address to check + const addr = currentStack.owners.getAddress(currentStack.index) + const addrStr = addr.toString("hex") + currentStack.index++ + // Is it a multi-sig address ? + const alias = this.msigAliases.get(addrStr) + if (alias !== undefined) { + if (stack.length > MaxSignatures) { + throw TooManySignatures + } + if (cycleCheck.has(addrStr)) { + throw new Error("cyclink multisig alias") + } + cycleCheck.add(addrStr) + stack.push({ + index: 0, + verified: 0, + addrVerifiedTotal: addrVerified, + parentVerified: + currentStack.parentVerified || + currentStack.verified >= currentStack.owners.getThreshold(), + owners: alias + }) + continue Stack + } else { + if ( + !currentStack.parentVerified && + currentStack.verified < currentStack.owners.getThreshold() + ) { + if (this.hasKey(addr)) { + if (addrVisited > MaxSignatures) { + throw TooManySignatures + } + + const sigIdx = new SigIdx() + sigIdx.setSource(addr) + helper.writeUIntBE(addrVisited, 0, 4) + sigIdx.fromBuffer(helper) + sigIdxs.push(sigIdx) + + currentStack.verified++ + addrVerified++ + } + } + addrVisited++ + } + } + + // remove head + stack.pop() + // verify current level + if (currentStack.verified < currentStack.owners.getThreshold()) { + if (stack.length == 0) { + throw new SignatureError("Not enough signatures") + } + // We recover to previous state + addrVerified = currentStack.addrVerifiedTotal + stack.splice(addrVerified) + } else if (stack.length > 0) { + currentStack = stack[stack.length - 1] + if (currentStack.verified < currentStack.owners.getThreshold()) { + // apply child verification + currentStack.verified++ + } + } + } + + this.sigIdxs.push(sigIdxs) + } + + constructor( + hrp: string, + chainID: string, + signedBytes: Buffer, + credTypeID: number, + txOwners?: OutputOwners[], + msigAliases?: Map + ) { super() - this.signedBytes = Buffer.from(signedBytes) this.hrp = hrp this.chainID = chainID + this.signedBytes = Buffer.from(signedBytes) + ;(this.credTypeID = credTypeID), (this.txOwners = txOwners ?? []) + this.msigAliases = msigAliases ?? new Map() } } diff --git a/src/common/output.ts b/src/common/output.ts index 7554c7067..230161586 100644 --- a/src/common/output.ts +++ b/src/common/output.ts @@ -218,6 +218,11 @@ export class OutputOwners extends Serializable { return result } + /** + * Returns an the length of the Addresses array. + */ + getAddressesLength = (): number => this.addresses.length + /** * Returns the index of the address. * @@ -386,6 +391,31 @@ export class OutputOwners extends Serializable { this.locktime = bintools.fromBNToBuffer(locktime, 8) } } + + static fromArray(b: Buffer): OutputOwners[] { + let offset = 6 //version + counter + let num = b.readUInt32BE(2) + const result: OutputOwners[] = [] + while (offset < b.length && num-- > 0) { + const t = new OutputOwners() + offset = t.fromBuffer(b, offset) + result.push(t) + } + return result + } + + static toArray(o: OutputOwners[]): Buffer { + const numOutputOwners = Buffer.alloc(6) + numOutputOwners.writeUInt32BE(o.length, 2) + let bsize: number = 6 + const barr: Buffer[] = [numOutputOwners] + for (const outputOwner of o) { + const b = outputOwner.toBuffer() + bsize += b.length + barr.push(b) + } + return Buffer.concat(barr, bsize) + } } export abstract class Output extends OutputOwners { diff --git a/src/common/tx.ts b/src/common/tx.ts index 2c1058785..41d254f0d 100644 --- a/src/common/tx.ts +++ b/src/common/tx.ts @@ -13,7 +13,6 @@ import { StandardParseableOutput, StandardTransferableOutput } from "./output" -import { MultisigAliasSet } from "./multisigkeychain" import { DefaultNetworkID } from "../utils/constants" import { Serializable, @@ -192,13 +191,6 @@ export abstract class StandardBaseTx< abstract select(id: number, ...args: any[]): this - resolveMultisigIndices(resolver: MultisigAliasSet) { - for (let i: number = 0; i < this.ins.length; i++) { - const input = this.ins[`${i}`].getInput() - input.setSigIdxs(resolver.resolveMultisig(input.getSigIdxs())) - } - } - /** * Class representing a StandardBaseTx which is the foundation for all transactions. * diff --git a/tests/apis/platformvm/api.test.ts b/tests/apis/platformvm/api.test.ts index f42886a34..8b0ccc1a2 100644 --- a/tests/apis/platformvm/api.test.ts +++ b/tests/apis/platformvm/api.test.ts @@ -52,6 +52,7 @@ import { import { ErrorResponseObject } from "../../../src/utils/errors" import { HttpResponse } from "jest-mock-axios/dist/lib/mock-axios-types" import { Builder } from "../../../src/apis/platformvm/builder" +import { ZeroBN } from "../../../src/common" /** * @ignore @@ -1219,7 +1220,10 @@ describe("PlatformVMAPI", (): void => { new BN(amnt), assetID, addrs3.map((a): Buffer => platformvm.parseAddress(a)), - addrs1.map((a): Buffer => platformvm.parseAddress(a)), + { + from: addrs1.map((a): Buffer => platformvm.parseAddress(a)), + signer: [] + }, addrs1.map((a): Buffer => platformvm.parseAddress(a)), platformvm.getTxFee(), assetID, @@ -1270,7 +1274,7 @@ describe("PlatformVMAPI", (): void => { networkID, bintools.cb58Decode(blockchainID), addrbuff3, - addrbuff1, + { from: addrbuff1, signer: [] }, addrbuff2, [fungutxo], bintools.cb58Decode(DefaultPlatformChainID), @@ -1337,7 +1341,7 @@ describe("PlatformVMAPI", (): void => { amount, assetID, addrbuff3, - addrbuff1, + { from: addrbuff1, signer: [] }, bintools.cb58Decode(TestXBlockchainID), addrbuff2, platformvm.getTxFee(), @@ -1368,7 +1372,7 @@ describe("PlatformVMAPI", (): void => { amount, assetID, addrbuff3, - addrbuff1, + { from: addrbuff1, signer: [] }, undefined, addrbuff2, platformvm.getTxFee(), @@ -1410,41 +1414,57 @@ describe("PlatformVMAPI", (): void => { serialzeit(tx1, "ExportTx") }) - /* - test('buildAddSubnetValidatorTx', async (): Promise => { - platformvm.setFee(new BN(fee)); - const addrbuff1 = addrs1.map((a) => platformvm.parseAddress(a)); - const addrbuff2 = addrs2.map((a) => platformvm.parseAddress(a)); - const addrbuff3 = addrs3.map((a) => platformvm.parseAddress(a)); - const amount:BN = new BN(90); + test("buildAddSubnetValidatorTx", async (): Promise => { + const subnetID = "2cXEvbdDaP6q6srB6x1T14raebpJaM4s2t9NE5kiXzLqLXQDWm" + const addrbuff1 = addrs1.map((a) => platformvm.parseAddress(a)) + const addrbuff2 = addrs2.map((a) => platformvm.parseAddress(a)) - const txu1:UnsignedTx = await platformvm.buildAddSubnetValidatorTx( + const txu1: UnsignedTx = await platformvm.buildAddSubnetValidatorTx( set, addrs1, addrs2, nodeID, startTime, endTime, - PlatformVMConstants.MINSTAKE, - new UTF8Payload("hello world"), UnixNow() - ); + avalanche.getNetwork().P.minStake, + subnetID, + new UTF8Payload("hello world"), + UnixNow(), + { + addresses: addrbuff1, + threshold: 1, + signer: [[0, addrbuff1[0]]] + } + ) - const txu2:UnsignedTx = set.buildAddSubnetValidatorTx( - networkID, bintools.cb58Decode(blockchainID), - addrbuff1, + const txu2: UnsignedTx = await builder.buildAddSubnetValidatorTx( + networkID, + bintools.cb58Decode(blockchainID), + { + from: addrbuff1, + signer: [] + }, addrbuff2, NodeIDStringToBuffer(nodeID), startTime, endTime, - PlatformVMConstants.MINSTAKE, - platformvm.getFee(), + avalanche.getNetwork().P.minStake, + subnetID, + new BN(avalanche.getNetwork().P.txFee ?? 0), assetID, - new UTF8Payload("hello world").getPayload(), UnixNow() - ); - expect(txu2.toBuffer().toString('hex')).toBe(txu1.toBuffer().toString('hex')); - expect(txu2.toString()).toBe(txu1.toString()); - }); - */ + new UTF8Payload("hello world").getPayload(), + UnixNow(), + { + addresses: addrbuff1, + threshold: 1, + signer: [[0, addrbuff1[0]]] + } + ) + expect(txu2.toBuffer().toString("hex")).toBe( + txu1.toBuffer().toString("hex") + ) + expect(txu2.toString()).toBe(txu1.toString()) + }) test("buildAddDelegatorTx 1", async (): Promise => { const addrbuff1 = addrs1.map((a) => platformvm.parseAddress(a)) @@ -1481,7 +1501,7 @@ describe("PlatformVMAPI", (): void => { bintools.cb58Decode(blockchainID), assetID, addrbuff3, - addrbuff1, + { from: addrbuff1, signer: [] }, addrbuff2, NodeIDStringToBuffer(nodeID), startTime, @@ -2096,7 +2116,7 @@ describe("PlatformVMAPI", (): void => { networkID, bintools.cb58Decode(blockchainID), addrbuff3, - addrbuff1, + { from: addrbuff1, signer: [] }, addrbuff2, NodeIDStringToBuffer(nodeID), startTime, @@ -2175,7 +2195,7 @@ describe("PlatformVMAPI", (): void => { bintools.cb58Decode(blockchainID), assetID, addrbuff3, - addrbuff1, + { from: addrbuff1, signer: [] }, addrbuff2, NodeIDStringToBuffer(nodeID), startTime, @@ -2250,7 +2270,7 @@ describe("PlatformVMAPI", (): void => { networkID, bintools.cb58Decode(blockchainID), addrbuff3, - addrbuff1, + { from: addrbuff1, signer: [] }, addrbuff2, NodeIDStringToBuffer(nodeID), startTime, @@ -2446,7 +2466,7 @@ describe("PlatformVMAPI", (): void => { const txu2: UnsignedTx = await builder.buildCreateSubnetTx( networkID, bintools.cb58Decode(blockchainID), - addrbuff1, + { from: addrbuff1, signer: [] }, addrbuff2, [addrbuff1[0]], 1, @@ -2509,7 +2529,7 @@ describe("PlatformVMAPI", (): void => { const txu2: UnsignedTx = await lbuilder.buildCreateSubnetTx( networkID, bintools.cb58Decode(blockchainID), - addrbuff1, + { from: addrbuff1, signer: [] }, addrbuff2, [addrbuff1[0]], 1, diff --git a/tests/apis/platformvm/tx.test.ts b/tests/apis/platformvm/tx.test.ts index 92798aff4..eb390dedc 100644 --- a/tests/apis/platformvm/tx.test.ts +++ b/tests/apis/platformvm/tx.test.ts @@ -409,7 +409,7 @@ describe("Transactions", (): void => { new BN(amnt * 1000), assetID, addrs3, - addrs1, + { from: addrs1, signer: [] }, addrs1 ) .catch((e) => expect(e).toBeDefined()) @@ -496,7 +496,7 @@ describe("Transactions", (): void => { new BN(9000), assetID, addrs3, - addrs1, + { from: addrs1, signer: [] }, addrs1, undefined, undefined, @@ -519,7 +519,7 @@ describe("Transactions", (): void => { new BN(9000), assetID, addrs3, - addrs1, + { from: addrs1, signer: [] }, addrs1 ) const tx: Tx = txu.sign(keymgr1) @@ -534,7 +534,7 @@ describe("Transactions", (): void => { netid, blockchainID, addrs3, - addrs1, + { from: addrs1, signer: [] }, addrs2, importUTXOs, bintools.cb58Decode(DefaultPlatformChainID), @@ -556,7 +556,7 @@ describe("Transactions", (): void => { new BN(90), avaxAssetID, addrs3, - addrs1, + { from: addrs1, signer: [] }, bintools.cb58Decode(DefaultPlatformChainID), addrs2, new BN(0),