From 6d6ecab241d088da98a43d1d46399d97db6cbf34 Mon Sep 17 00:00:00 2001 From: knikos Date: Tue, 27 Jun 2023 13:28:34 +0300 Subject: [PATCH] [Examples] Add new examples for AddDepositOfferTx and DepositTx + minor refactorings --- .../platformvm/buildAddDepositOfferTx-Msig.ts | 131 ++++++++++++++++++ examples/platformvm/buildAddDepositOfferTx.ts | 94 +++++++++++++ examples/platformvm/buildAddressStateTx.ts | 1 + .../platformvm/buildDepositTx-v1-MsigOwner.ts | 103 ++++++++++++++ examples/platformvm/buildDepositTx.ts | 1 + examples/platformvm/getUpgradePhases.ts | 1 - src/apis/platformvm/api.ts | 2 +- tests/apis/platformvm/addressstatetx.test.ts | 113 ++++++++++++++- tests/apis/platformvm/api.test.ts | 8 +- 9 files changed, 450 insertions(+), 4 deletions(-) create mode 100644 examples/platformvm/buildAddDepositOfferTx-Msig.ts create mode 100644 examples/platformvm/buildAddDepositOfferTx.ts create mode 100644 examples/platformvm/buildDepositTx-v1-MsigOwner.ts diff --git a/examples/platformvm/buildAddDepositOfferTx-Msig.ts b/examples/platformvm/buildAddDepositOfferTx-Msig.ts new file mode 100644 index 000000000..bb9213d54 --- /dev/null +++ b/examples/platformvm/buildAddDepositOfferTx-Msig.ts @@ -0,0 +1,131 @@ +import { Avalanche, BinTools, Buffer } from "caminojs/index" +import { + PlatformVMAPI, + KeyChain, + UnsignedTx, + Tx, + DepositOffer, + OfferFlag, + PlatformVMConstants +} from "caminojs/apis/platformvm" +import { UnixNow, PChainAlias } from "caminojs/utils" +import { ExamplesConfig } from "../common/examplesConfig" +import BN from "bn.js" +import { + MultisigKeyChain, + MultisigKeyPair, + OutputOwners +} from "caminojs/common" +import createHash from "create-hash" + +const config: ExamplesConfig = require("../common/examplesConfig.json") +const avalanche: Avalanche = new Avalanche( + config.host, + config.port, + config.protocol, + config.networkID +) +const bintools = BinTools.getInstance() +const multiSigAliasMember1PrivateKey = + "PrivateKey-2Vtf2ZhTRz6WcVcSH7cS7ghKneZxZ2L5W8assdCcaNDVdpoYfY" // P-kopernikus1jla8ty5c9ud6lsj8s4re2dvzvfxpzrxdcrd8q7 +const msigAlias = "P-kopernikus1fwrv3kj5jqntuucw67lzgu9a9tkqyczxgcvpst" +const depositOwner = "P-kopernikus13kyf72ftu4l77kss7xm0kshm0au29s48zjaygq" + +const depositOwnerPrivateKey = + "PrivateKey-Ge71NJhUY3TjZ9dLohijSnNq46QxobjqxHGMUDAPoVsNFA93w" +let pchain: PlatformVMAPI +let pKeychain: KeyChain +let pAddresses: Buffer[] +let pAddressStrings: string[] + +const InitAvalanche = async () => { + await avalanche.fetchNetworkSettings() + pchain = avalanche.PChain() + pKeychain = pchain.keyChain() + pKeychain.importKey(multiSigAliasMember1PrivateKey) + pKeychain.importKey(depositOwnerPrivateKey) + + pAddresses = pchain.keyChain().getAddresses() + pAddressStrings = pchain.keyChain().getAddressStrings() +} + +const main = async (): Promise => { + await InitAvalanche() + + const msigAliasBuffer = pchain.parseAddress(msigAlias) + const owner = await pchain.getMultisigAlias(msigAlias) + const startTime = UnixNow().add(new BN(600 * 1)) + const endTime: BN = startTime.add(new BN(26300000)) + + const depositOfferMsigCreator = msigAlias // Warning: this address must have the role OFFERS_CREATOR + const depositOfferMsigCreatorAuth: [number, string | Buffer][] = [ + [0, depositOfferMsigCreator] + ] + const offer: DepositOffer = { + upgradeVersion: 1, + id: undefined, + interestRateNominator: new BN(1000000000), + start: startTime, + end: endTime, + minAmount: new BN(1000000), // min deposit amount = 1 milliAvax + totalMaxAmount: undefined, + depositedAmount: undefined, + minDuration: 60, + maxDuration: 360, + unlockPeriodDuration: 20, + noRewardsPeriodDuration: 10, + memo: "post-genesis-deposit-offer", + flags: new BN(OfferFlag.NONE), + totalMaxRewardAmount: new BN(1000000), + rewardedAmount: new BN(0), + ownerAddress: depositOwner + } + const unsignedTx: UnsignedTx = await pchain.buildAddDepositOfferTx( + undefined, + [[depositOfferMsigCreator], pAddressStrings], + [depositOfferMsigCreator], + offer, + depositOfferMsigCreator, + depositOfferMsigCreatorAuth, + Buffer.from("new-deposit-offer") + ) + + // Create the hash from the tx + const txbuff = unsignedTx.toBuffer() + const msg: Buffer = Buffer.from(createHash("sha256").update(txbuff).digest()) + + // create MSKeychain to create proper sigindx + const msKeyChain = new MultisigKeyChain( + avalanche.getHRP(), + PChainAlias, + msg, + PlatformVMConstants.SECPMULTISIGCREDENTIAL, + unsignedTx.getTransaction().getOutputOwners(), + new Map([ + [ + msigAliasBuffer.toString("hex"), + new OutputOwners( + owner.addresses.map((a) => bintools.parseAddress(a, "P")), + new BN(owner.locktime), + owner.threshold + ) + ] + ]) + ) + + for (let address of pAddresses) { + // We need the keychain for signing + const keyPair = pKeychain.getKey(address) + // The signature + const signature = keyPair.sign(msg) + // add the signature + msKeyChain.addKey(new MultisigKeyPair(msKeyChain, address, signature)) + } + + msKeyChain.buildSignatureIndices() + const tx: Tx = unsignedTx.sign(msKeyChain) + const txid: string = await pchain.issueTx(tx) + console.log(`Success! TXID: ${txid}`) +} + +main() diff --git a/examples/platformvm/buildAddDepositOfferTx.ts b/examples/platformvm/buildAddDepositOfferTx.ts new file mode 100644 index 000000000..01c873f9e --- /dev/null +++ b/examples/platformvm/buildAddDepositOfferTx.ts @@ -0,0 +1,94 @@ +import { Avalanche, Buffer } from "caminojs/index" +import { + PlatformVMAPI, + KeyChain, + UnsignedTx, + Tx, + DepositOffer, + OfferFlag +} from "caminojs/apis/platformvm" +import { + PrivateKeyPrefix, + DefaultLocalGenesisPrivateKey, + UnixNow +} from "caminojs/utils" +import { ExamplesConfig } from "../common/examplesConfig" +import BN from "bn.js" + +const config: ExamplesConfig = require("../common/examplesConfig.json") +const avalanche: Avalanche = new Avalanche( + config.host, + config.port, + config.protocol, + config.networkID +) + +/** + * @ignore + */ +let privKey: string = `${PrivateKeyPrefix}${DefaultLocalGenesisPrivateKey}` +const ownerPrivKey: string = + "PrivateKey-Ge71NJhUY3TjZ9dLohijSnNq46QxobjqxHGMUDAPoVsNFA93w" +let pchain: PlatformVMAPI +let pKeychain: KeyChain +let pAddresses: Buffer[] +let pAddressStrings: string[] + +const InitAvalanche = async () => { + await avalanche.fetchNetworkSettings() + pchain = avalanche.PChain() + pKeychain = pchain.keyChain() + // P-local18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p + pKeychain.importKey(privKey) + // P-kopernikus13kyf72ftu4l77kss7xm0kshm0au29s48zjaygq + pKeychain.importKey(ownerPrivKey) + + pAddresses = pchain.keyChain().getAddresses() + pAddressStrings = pchain.keyChain().getAddressStrings() +} + +const main = async (): Promise => { + await InitAvalanche() + const startTime = UnixNow().add(new BN(600 * 1)) + const endTime: BN = startTime.add(new BN(26300000)) + + const depositOfferCreator = pAddressStrings[0] // Warning: this address must have the role OFFERS_CREATOR + const depositOfferCreatorAuth: [number, string | Buffer][] = [ + [0, depositOfferCreator] + ] + const depositOwner = pAddressStrings[1] + const offer: DepositOffer = { + upgradeVersion: 1, + id: undefined, + interestRateNominator: new BN(1000000000), + start: startTime, + end: endTime, + minAmount: new BN(1000000), // min deposit amount = 1 milliAvax + totalMaxAmount: undefined, + depositedAmount: undefined, + minDuration: 60, + maxDuration: 360, + unlockPeriodDuration: 20, + noRewardsPeriodDuration: 10, + memo: "post-genesis-deposit-offer", + flags: new BN(OfferFlag.NONE), + totalMaxRewardAmount: new BN(1000000), + rewardedAmount: new BN(0), + ownerAddress: depositOwner + } + const unsignedTx: UnsignedTx = await pchain.buildAddDepositOfferTx( + undefined, + [depositOfferCreator], + [depositOfferCreator], + offer, + depositOfferCreator, + depositOfferCreatorAuth, + Buffer.from("new-deposit-offer") + ) + + const tx: Tx = unsignedTx.sign(pKeychain) + const txid: string = await pchain.issueTx(tx) + console.log(`Success! TXID: ${txid}`) +} + +main() diff --git a/examples/platformvm/buildAddressStateTx.ts b/examples/platformvm/buildAddressStateTx.ts index 10cbadc0c..2fc3efe0f 100644 --- a/examples/platformvm/buildAddressStateTx.ts +++ b/examples/platformvm/buildAddressStateTx.ts @@ -47,6 +47,7 @@ const main = async (): Promise => { ) const unsignedTx: UnsignedTx = await pchain.buildAddressStateTx( + 0, undefined, pAddressStrings, pAddressStrings, diff --git a/examples/platformvm/buildDepositTx-v1-MsigOwner.ts b/examples/platformvm/buildDepositTx-v1-MsigOwner.ts new file mode 100644 index 000000000..8f0c1743b --- /dev/null +++ b/examples/platformvm/buildDepositTx-v1-MsigOwner.ts @@ -0,0 +1,103 @@ +import { Avalanche, BinTools, Buffer } from "caminojs/index" +import { + PlatformVMAPI, + KeyChain, + UnsignedTx, + Tx +} from "caminojs/apis/platformvm" +import { OutputOwners } from "caminojs/common/output" +import { PrivateKeyPrefix, DefaultLocalGenesisPrivateKey } from "caminojs/utils" +import { ExamplesConfig } from "../common/examplesConfig" +import BN from "bn.js" +import createHash from "create-hash" +import { SignerKeyPair } from "caminojs/common" + +const config: ExamplesConfig = require("../common/examplesConfig.json") +const avalanche: Avalanche = new Avalanche( + config.host, + config.port, + config.protocol, + config.networkID +) + +const msigAlias = "P-kopernikus1fwrv3kj5jqntuucw67lzgu9a9tkqyczxgcvpst" +const multiSigAliasMember1PrivateKey = + "PrivateKey-2Vtf2ZhTRz6WcVcSH7cS7ghKneZxZ2L5W8assdCcaNDVdpoYfY" // P-kopernikus1jla8ty5c9ud6lsj8s4re2dvzvfxpzrxdcrd8q7 + +const bintools: BinTools = BinTools.getInstance() +const privKey: string = `${PrivateKeyPrefix}${DefaultLocalGenesisPrivateKey}` +let pchain: PlatformVMAPI +let pKeychain: KeyChain +let pAddressStrings: string[] + +const InitAvalanche = async () => { + await avalanche.fetchNetworkSettings() + pchain = avalanche.PChain() + pKeychain = pchain.keyChain() + // P-local18jma8ppw3nhx5r4ap8clazz0dps7rv5u9xde7p + pKeychain.importKey(privKey) + pKeychain.importKey(multiSigAliasMember1PrivateKey) + pAddressStrings = pchain.keyChain().getAddressStrings() +} + +const main = async (): Promise => { + await InitAvalanche() + const depositOwner = msigAlias + const msigAliasBuffer = pchain.parseAddress(msigAlias) + const amountToLock = new BN(10000000) + const depositOfferID = "27pLgKVoXqtSWqGtBTtGkVKmZUM3MYTsjDjNzuLEr9gWiVGXy3" + const depositDuration = 110 + const memo: Buffer = Buffer.from("DepositTx v1 with msig deposit offer owner") + const owners = new OutputOwners( + pchain.keyChain().getAddresses(), + undefined, + 1 + ) + + const depositOfferCreator = pAddressStrings[0] // Warning: this address must have the role OFFERS_CREATOR + const depositOfferCreatorAuth: [number, string | Buffer][] = [ + [0, depositOfferCreator] + ] + + const depositOfferOwnerAuth: [number, string | Buffer][] = [[0, depositOwner]] + + // hash concatenated bytes of offer id and deposit owner address + const msgHashed: Buffer = Buffer.from( + createHash("sha256") + .update( + Buffer.concat([ + bintools.cb58Decode(depositOfferID), + pchain.parseAddress(depositOfferCreator) + ]) + ) + .digest() + ) + const keypair: SignerKeyPair = pKeychain.getKey( + pKeychain.getAddresses()[1] // msig member 1 + ) + // sign the hash + const signatureBuffer: Buffer = keypair.sign(msgHashed) + + const unsignedTx: UnsignedTx = await pchain.buildDepositTx( + 1, + undefined, + pAddressStrings, + pAddressStrings, + depositOfferID, + depositDuration, + owners, + depositOfferCreator, + depositOfferCreatorAuth, + [signatureBuffer], + depositOfferOwnerAuth, + memo, + new BN(0), + amountToLock + ) + + const tx: Tx = unsignedTx.sign(pKeychain) + const txid: string = await pchain.issueTx(tx) + console.log(`Success! TXID: ${txid}`) +} + +main() diff --git a/examples/platformvm/buildDepositTx.ts b/examples/platformvm/buildDepositTx.ts index 1229224e1..5f401d4b0 100644 --- a/examples/platformvm/buildDepositTx.ts +++ b/examples/platformvm/buildDepositTx.ts @@ -53,6 +53,7 @@ const main = async (): Promise => { ) const unsignedTx: UnsignedTx = await pchain.buildDepositTx( + 0, undefined, pAddressStrings, pAddressStrings, diff --git a/examples/platformvm/getUpgradePhases.ts b/examples/platformvm/getUpgradePhases.ts index 045eddfbb..b8d942aef 100644 --- a/examples/platformvm/getUpgradePhases.ts +++ b/examples/platformvm/getUpgradePhases.ts @@ -1,7 +1,6 @@ import { Avalanche } from "caminojs/index" import { PlatformVMAPI } from "caminojs/apis/platformvm" import { ExamplesConfig } from "../common/examplesConfig" -import { json } from "stream/consumers" const config: ExamplesConfig = require("../common/examplesConfig.json") const avalanche: Avalanche = new Avalanche( diff --git a/src/apis/platformvm/api.ts b/src/apis/platformvm/api.ts index 81e9e7bb7..1a007be2f 100644 --- a/src/apis/platformvm/api.ts +++ b/src/apis/platformvm/api.ts @@ -2554,7 +2554,7 @@ export class PlatformVMAPI extends JRPCAPI { amountToLock: BN, changeThreshold: number = 1 ): Promise => { - const caller = "buildRegisterNodeTx" + const caller = "buildDepositTx" const fromSigner = this._parseFromSigner(fromAddresses, caller) diff --git a/tests/apis/platformvm/addressstatetx.test.ts b/tests/apis/platformvm/addressstatetx.test.ts index dd37e2638..dfaad7cfd 100644 --- a/tests/apis/platformvm/addressstatetx.test.ts +++ b/tests/apis/platformvm/addressstatetx.test.ts @@ -1,6 +1,10 @@ import BN from "bn.js" import { Buffer } from "buffer/" -import { AddressState, PlatformVMConstants } from "src/apis/platformvm" +import { + AddressState, + PlatformVMConstants, + SubnetAuth +} from "src/apis/platformvm" import { AddressStateTx } from "src/apis/platformvm/addressstatetx" import BinTools from "src/utils/bintools" import { DefaultNetworkID, Serialization } from "src/utils" @@ -84,6 +88,113 @@ describe("AddressStateTx", (): void => { }) }) +describe("AddressStateTxV1", (): void => { + const addressStateAddress = "P-local19rknw8l0grnfunjrzwxlxync6zrlu33ynpm3qq" + const executorAddress = "P-local1wst8jt3z3fm9ce0z6akj3266zmgccdp0gy0e76" + const addressStateTx = new AddressStateTx( + 1, + 1, + Buffer.alloc(32, 16), + [], + [], + Buffer.from("addressStateTx-v1"), + addressStateAddress, + AddressState.KYC_EXPIRED, + true, + executorAddress, + undefined + ) + const addressStateTxHex: string = + "ffffffffffff000100000001101010101010101010101010101010101010101010101010101010101010101000000000000000000000001161646472657373537461746554782d763128ed371fef40e69e4e43138df31278d087fe462421017416792e228a765c65e2d76d28ab5a16d18c342f0000000a00000000" + const addressStateTxBuf: Buffer = Buffer.from(addressStateTxHex, "hex") + // const addressStateTx: AddressStateTx = new AddressStateTx() + addressStateTx.fromBuffer(addressStateTxBuf) + + test("getTypeName", async (): Promise => { + const addressStateTxTypeName: string = addressStateTx.getTypeName() + expect(addressStateTxTypeName).toBe("AddressStateTx") + }) + + test("getTypeID", async (): Promise => { + const addressStateTxTypeID: number = addressStateTx.getTypeID() + expect(addressStateTxTypeID).toBe(PlatformVMConstants.ADDRESSSTATETX) + }) + + test("toBuffer and fromBuffer", async (): Promise => { + const buf: Buffer = addressStateTx.toBuffer() + const asvTx: AddressStateTx = new AddressStateTx() + asvTx.fromBuffer(buf) + const buf2: Buffer = asvTx.toBuffer() + expect(buf.toString("hex")).toBe(buf2.toString("hex")) + }) + + test("serialize", async (): Promise => { + const serializedAddressStateTx: object = addressStateTx.serialize() + + const expectedJSON = { + _codecID: null, + _typeID: PlatformVMConstants.ADDRESSSTATETX, + _typeName: "AddressStateTx", + address: serialization + .typeToBuffer(addressStateAddress, "bech32") + .toString("hex"), + blockchainID: serialization.encoder( + Buffer.alloc(32, 16), + "hex", + "Buffer", + "cb58" + ), + ins: [], + memo: serialization + .typeToBuffer( + bintools.cb58Encode(Buffer.from("addressStateTx-v1")), + "cb58" + ) + .toString("hex"), + networkID: String(DefaultNetworkID).padStart(8, "0"), + outs: [], + state: AddressState.KYC_EXPIRED, + remove: true, + executor: serialization + .typeToBuffer(executorAddress, "bech32") + .toString("hex"), + executorAuth: new SubnetAuth().serialize() + } + expect(serializedAddressStateTx).toStrictEqual(expectedJSON) + }) + + test("getAddress", async (): Promise => { + const expectedAddress: Buffer = bintools.stringToAddress( + "X-local19rknw8l0grnfunjrzwxlxync6zrlu33ynpm3qq", + "local" + ) + const address: Buffer = addressStateTx.getAddress() + expect(address.toString()).toBe(expectedAddress.toString()) + }) + + test("getState", async (): Promise => { + const expectedState = AddressState.KYC_EXPIRED + expect(addressStateTx.getState()).toBe(expectedState) + }) + + test("getRemove", async (): Promise => { + const expectedRemove = true + expect(addressStateTx.getRemove()).toBe(expectedRemove) + }) + test("getExecutor", async (): Promise => { + const expectedExecutor: Buffer = bintools.stringToAddress( + executorAddress, + "local" + ) + const address: Buffer = addressStateTx.getExecutor() + expect(address.toString()).toBe(expectedExecutor.toString()) + }) + test("getExecutorAuth", async (): Promise => { + const actualAuth: SubnetAuth = addressStateTx.getExecutorAuth() + expect(actualAuth).toStrictEqual(new SubnetAuth()) + }) +}) + describe("AddressStateTxV1", (): void => { const addressStateAddress = "P-local19rknw8l0grnfunjrzwxlxync6zrlu33ynpm3qq" const executorAddress = "P-local1wst8jt3z3fm9ce0z6akj3266zmgccdp0gy0e76" diff --git a/tests/apis/platformvm/api.test.ts b/tests/apis/platformvm/api.test.ts index 5cbf22665..5ce320049 100644 --- a/tests/apis/platformvm/api.test.ts +++ b/tests/apis/platformvm/api.test.ts @@ -2856,12 +2856,17 @@ describe("PlatformVMAPI", (): void => { 1 ) const result = api.buildDepositTx( + undefined, undefined, [defaultAddr], [defaultAddr], depositOfferID, depositDuration, rewardsOwner, + undefined, // empty depositCreatorAddress + [], // empty depositCreatorAuth + undefined, // empty depositOfferOwnerSigs + [], // empty depositOfferOwnerAuth Buffer.from("memo"), ZeroBN, new BN(1), @@ -2870,12 +2875,13 @@ describe("PlatformVMAPI", (): void => { const txu1 = await result const expectedDepositTx = new DepositTx( + undefined, networkID, Buffer.alloc(32, 0), spendResponse.out, spendResponse.ins, Buffer.from("memo"), - depositOfferID, + bintools.cb58Decode(depositOfferID), depositDuration, new ParseableOutput(rewardOutputOwners) )