diff --git a/CHANGELOG.md b/CHANGELOG.md index ce57907f8..5b2dafefe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ ### Update - refactor!: remove the constructor from `KeyPair`, use `KeyPair.fromSecretSeed` or `KeyPair.fromAccountId` instead. - fix!: fix bug with signing auth entries in multi-sig scenarios. +- feat!: support constructors in contract creation via `InvokeHostFunctionOperation.createContractOperationBuilder`, the signature of the function has been changed. ## 1.0.0-alpha0 We are thrilled to announce the release of version 1.0.0-alpha0 for java-stellar-sdk, diff --git a/src/main/java/org/stellar/sdk/operations/InvokeHostFunctionOperation.java b/src/main/java/org/stellar/sdk/operations/InvokeHostFunctionOperation.java index cead365c2..e2cb02bab 100644 --- a/src/main/java/org/stellar/sdk/operations/InvokeHostFunctionOperation.java +++ b/src/main/java/org/stellar/sdk/operations/InvokeHostFunctionOperation.java @@ -21,6 +21,7 @@ import org.stellar.sdk.xdr.ContractIDPreimage; import org.stellar.sdk.xdr.ContractIDPreimageType; import org.stellar.sdk.xdr.CreateContractArgs; +import org.stellar.sdk.xdr.CreateContractArgsV2; import org.stellar.sdk.xdr.Hash; import org.stellar.sdk.xdr.HostFunction; import org.stellar.sdk.xdr.HostFunctionType; @@ -91,14 +92,18 @@ public static InvokeHostFunctionOperation fromXdr(InvokeHostFunctionOp op) { * * @param wasmId The hex-encoded wasm id to use for contract creation. * @param address The address to use to derive the contract ID. + * @param constructorArgs The optional parameters to pass to the constructor of this contract. * @param salt The 32-byte salt to use to derive the contract ID, if null, a random salt will be * generated. * @return {@link InvokeHostFunctionOperationBuilder} */ public static InvokeHostFunctionOperationBuilder createContractOperationBuilder( - String wasmId, Address address, @Nullable byte[] salt) { + String wasmId, + Address address, + @Nullable Collection constructorArgs, + @Nullable byte[] salt) { byte[] wasmIdBytes = Util.hexToBytes(wasmId); - return createContractOperationBuilder(wasmIdBytes, address, salt); + return createContractOperationBuilder(wasmIdBytes, address, constructorArgs, salt); } /** @@ -108,12 +113,16 @@ public static InvokeHostFunctionOperation fromXdr(InvokeHostFunctionOp op) { * * @param wasmId The wasm id to use for contract creation. * @param address The address to use to derive the contract ID. + * @param constructorArgs The optional parameters to pass to the constructor of this contract. * @param salt The 32-byte salt to use to derive the contract ID, if null, a random salt will be * generated. * @return {@link InvokeHostFunctionOperationBuilder} */ public static InvokeHostFunctionOperationBuilder createContractOperationBuilder( - byte[] wasmId, Address address, @Nullable byte[] salt) { + byte[] wasmId, + Address address, + @Nullable Collection constructorArgs, + @Nullable byte[] salt) { if (salt == null) { salt = new byte[32]; new SecureRandom().nextBytes(salt); @@ -125,8 +134,8 @@ public static InvokeHostFunctionOperation fromXdr(InvokeHostFunctionOp op) { throw new IllegalArgumentException("\"wasmId\" must be 32 bytes long"); } - CreateContractArgs createContractArgs = - CreateContractArgs.builder() + CreateContractArgsV2 createContractArgs = + CreateContractArgsV2.builder() .contractIDPreimage( ContractIDPreimage.builder() .discriminant(ContractIDPreimageType.CONTRACT_ID_PREIMAGE_FROM_ADDRESS) @@ -141,11 +150,13 @@ public static InvokeHostFunctionOperation fromXdr(InvokeHostFunctionOp op) { .discriminant(ContractExecutableType.CONTRACT_EXECUTABLE_WASM) .wasm_hash(new Hash(wasmId)) .build()) + .constructorArgs( + constructorArgs != null ? constructorArgs.toArray(new SCVal[0]) : new SCVal[0]) .build(); HostFunction hostFunction = HostFunction.builder() - .discriminant(HostFunctionType.HOST_FUNCTION_TYPE_CREATE_CONTRACT) - .createContract(createContractArgs) + .discriminant(HostFunctionType.HOST_FUNCTION_TYPE_CREATE_CONTRACT_V2) + .createContractV2(createContractArgs) .build(); return builder().hostFunction(hostFunction); } diff --git a/src/test/java/org/stellar/sdk/operations/InvokeHostFunctionOperationTest.java b/src/test/java/org/stellar/sdk/operations/InvokeHostFunctionOperationTest.java index ef82aeeaf..5da854ec3 100644 --- a/src/test/java/org/stellar/sdk/operations/InvokeHostFunctionOperationTest.java +++ b/src/test/java/org/stellar/sdk/operations/InvokeHostFunctionOperationTest.java @@ -5,6 +5,8 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import java.math.BigInteger; +import java.util.Arrays; import java.util.Collections; import java.util.List; import org.junit.Test; @@ -18,6 +20,7 @@ import org.stellar.sdk.xdr.ContractIDPreimage; import org.stellar.sdk.xdr.ContractIDPreimageType; import org.stellar.sdk.xdr.CreateContractArgs; +import org.stellar.sdk.xdr.CreateContractArgsV2; import org.stellar.sdk.xdr.Hash; import org.stellar.sdk.xdr.HostFunction; import org.stellar.sdk.xdr.HostFunctionType; @@ -322,11 +325,12 @@ public void createContractOperationBuilderWithWasmIdString() { }; Address address = new Address("GAHJJJKMOKYE4RVPZEWZTKH5FVI4PA3VL7GK2LFNUBSGBV6OJP7TQSLX"); InvokeHostFunctionOperation operation = - InvokeHostFunctionOperation.createContractOperationBuilder(wasmIdString, address, salt) + InvokeHostFunctionOperation.createContractOperationBuilder( + wasmIdString, address, null, salt) .build(); - CreateContractArgs createContractArgs = - CreateContractArgs.builder() + CreateContractArgsV2 createContractArgs = + CreateContractArgsV2.builder() .contractIDPreimage( ContractIDPreimage.builder() .discriminant(ContractIDPreimageType.CONTRACT_ID_PREIMAGE_FROM_ADDRESS) @@ -341,18 +345,19 @@ public void createContractOperationBuilderWithWasmIdString() { .discriminant(ContractExecutableType.CONTRACT_EXECUTABLE_WASM) .wasm_hash(new Hash(wasmId)) .build()) + .constructorArgs(new SCVal[0]) .build(); HostFunction expectedFunction = HostFunction.builder() - .discriminant(HostFunctionType.HOST_FUNCTION_TYPE_CREATE_CONTRACT) - .createContract(createContractArgs) + .discriminant(HostFunctionType.HOST_FUNCTION_TYPE_CREATE_CONTRACT_V2) + .createContractV2(createContractArgs) .build(); assertEquals(operation.getHostFunction(), expectedFunction); assertTrue(operation.getAuth().isEmpty()); assertNull(operation.getSourceAccount()); String expectedXdr = - "AAAAAAAAABgAAAABAAAAAAAAAAAAAAAADpSlTHKwTkavyS2ZqP0tUceDdV/MrSytoGRg185L/zgRMwIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHwAAAAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHwAAAAA="; + "AAAAAAAAABgAAAADAAAAAAAAAAAAAAAADpSlTHKwTkavyS2ZqP0tUceDdV/MrSytoGRg185L/zgRMwIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHwAAAAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHwAAAAAAAAAA"; assertEquals(expectedXdr, operation.toXdrBase64()); } @@ -372,10 +377,11 @@ public void createContractOperationBuilderWithWasmIdBytes() { }; Address address = new Address("GAHJJJKMOKYE4RVPZEWZTKH5FVI4PA3VL7GK2LFNUBSGBV6OJP7TQSLX"); InvokeHostFunctionOperation operation = - InvokeHostFunctionOperation.createContractOperationBuilder(wasmId, address, salt).build(); + InvokeHostFunctionOperation.createContractOperationBuilder(wasmId, address, null, salt) + .build(); - CreateContractArgs createContractArgs = - CreateContractArgs.builder() + CreateContractArgsV2 createContractArgs = + CreateContractArgsV2.builder() .contractIDPreimage( ContractIDPreimage.builder() .discriminant(ContractIDPreimageType.CONTRACT_ID_PREIMAGE_FROM_ADDRESS) @@ -390,18 +396,76 @@ public void createContractOperationBuilderWithWasmIdBytes() { .discriminant(ContractExecutableType.CONTRACT_EXECUTABLE_WASM) .wasm_hash(new Hash(wasmId)) .build()) + .constructorArgs(new SCVal[0]) .build(); HostFunction expectedFunction = HostFunction.builder() - .discriminant(HostFunctionType.HOST_FUNCTION_TYPE_CREATE_CONTRACT) - .createContract(createContractArgs) + .discriminant(HostFunctionType.HOST_FUNCTION_TYPE_CREATE_CONTRACT_V2) + .createContractV2(createContractArgs) + .build(); + + assertEquals(operation.getHostFunction(), expectedFunction); + assertTrue(operation.getAuth().isEmpty()); + assertNull(operation.getSourceAccount()); + String expectedXdr = + "AAAAAAAAABgAAAADAAAAAAAAAAAAAAAADpSlTHKwTkavyS2ZqP0tUceDdV/MrSytoGRg185L/zgRMwIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHwAAAAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHwAAAAAAAAAA"; + assertEquals(expectedXdr, operation.toXdrBase64()); + } + + @Test + public void createContractOperationBuilderWithConstructorArgs() { + byte[] wasmId = + new byte[] { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f + }; + String wasmIdString = Util.bytesToHex(wasmId); + byte[] salt = + new byte[] { + 0x11, 0x33, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, + 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f + }; + Address address = new Address("GAHJJJKMOKYE4RVPZEWZTKH5FVI4PA3VL7GK2LFNUBSGBV6OJP7TQSLX"); + List constructorArgs = + Arrays.asList( + Scv.toAddress("GA2KQTETIRREL66P64GV6KCVICPULLDVHWJDZSIJKDLIAGBXUCIZ6P6E"), + Scv.toUint64(BigInteger.valueOf(123456789L))); + InvokeHostFunctionOperation operation = + InvokeHostFunctionOperation.createContractOperationBuilder( + wasmIdString, address, constructorArgs, salt) + .build(); + + CreateContractArgsV2 createContractArgs = + CreateContractArgsV2.builder() + .contractIDPreimage( + ContractIDPreimage.builder() + .discriminant(ContractIDPreimageType.CONTRACT_ID_PREIMAGE_FROM_ADDRESS) + .fromAddress( + ContractIDPreimage.ContractIDPreimageFromAddress.builder() + .address(address.toSCAddress()) + .salt(new Uint256(salt)) + .build()) + .build()) + .executable( + ContractExecutable.builder() + .discriminant(ContractExecutableType.CONTRACT_EXECUTABLE_WASM) + .wasm_hash(new Hash(wasmId)) + .build()) + .constructorArgs(constructorArgs.toArray(new SCVal[0])) + .build(); + HostFunction expectedFunction = + HostFunction.builder() + .discriminant(HostFunctionType.HOST_FUNCTION_TYPE_CREATE_CONTRACT_V2) + .createContractV2(createContractArgs) .build(); assertEquals(operation.getHostFunction(), expectedFunction); assertTrue(operation.getAuth().isEmpty()); assertNull(operation.getSourceAccount()); String expectedXdr = - "AAAAAAAAABgAAAABAAAAAAAAAAAAAAAADpSlTHKwTkavyS2ZqP0tUceDdV/MrSytoGRg185L/zgRMwIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHwAAAAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHwAAAAA="; + "AAAAAAAAABgAAAADAAAAAAAAAAAAAAAADpSlTHKwTkavyS2ZqP0tUceDdV/MrSytoGRg185L/zgRMwIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHwAAAAAAAQIDBAUGBwgJCgsMDQ4PEBESExQVFhcYGRobHB0eHwAAAAIAAAASAAAAAAAAAAA0qEyTRGJF+8/3DV8oVUCfRax1PZI8yQlQ1oAYN6CRnwAAAAUAAAAAB1vNFQAAAAA="; assertEquals(expectedXdr, operation.toXdrBase64()); }