Skip to content

Commit

Permalink
feat!: support constructors in contract creation via `InvokeHostFunct…
Browse files Browse the repository at this point in the history
…ionOperation.createContractOperationBuilder` (#642)
  • Loading branch information
overcat authored Oct 4, 2024
1 parent 90b4fe5 commit 0641dc4
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 19 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<SCVal> constructorArgs,
@Nullable byte[] salt) {
byte[] wasmIdBytes = Util.hexToBytes(wasmId);
return createContractOperationBuilder(wasmIdBytes, address, salt);
return createContractOperationBuilder(wasmIdBytes, address, constructorArgs, salt);
}

/**
Expand All @@ -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<SCVal> constructorArgs,
@Nullable byte[] salt) {
if (salt == null) {
salt = new byte[32];
new SecureRandom().nextBytes(salt);
Expand All @@ -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)
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down Expand Up @@ -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)
Expand All @@ -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());
}

Expand All @@ -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)
Expand All @@ -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<SCVal> 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());
}

Expand Down

0 comments on commit 0641dc4

Please sign in to comment.