Skip to content

Commit

Permalink
introduce func smart contracts' compilation without leaving your IDE.
Browse files Browse the repository at this point in the history
add GenericSmartContract where user can specify any codeCell and dataCell.
todo: test on other platforms, detect FIFTPATH automatically.
  • Loading branch information
neodix42 committed Jun 2, 2024
1 parent 5d70f20 commit 60cbbea
Show file tree
Hide file tree
Showing 9 changed files with 1,138 additions and 170 deletions.
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,11 @@
<artifactId>commons-lang3</artifactId>
<version>${lang3.version}</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down
1 change: 0 additions & 1 deletion smartcontract/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>${commons-io.version}</version>
<scope>test</scope>
</dependency>
<!-- recompiled from source with Java 8 -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
package org.ton.java.smartcontract;

import lombok.Builder;
import lombok.Getter;
import lombok.extern.java.Log;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.ton.java.utils.Utils;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import static java.util.Objects.nonNull;

/**
* Make sure you have fift and func installed. See <a href="https://github.com/ton-blockchain/packages">packages</a> for instructions.
*/
@Builder
@Getter
@Log
public class FuncCompiler {

String contractPath;
String funcExecutablePath;
String fiftExecutablePath;
String fiftAsmLibraryPath;
String fiftSmartcontLibraryPath;

public static class FuncCompilerBuilder {
}

public static FuncCompilerBuilder builder() {
return new CustomFuncCompilerBuilder();
}

private static class CustomFuncCompilerBuilder extends FuncCompilerBuilder {
@Override
public FuncCompiler build() {

return super.build();
}
}

/**
* @return code of BoC in hex
*/
public String compile() throws IOException, ExecutionException, InterruptedException {

String outputFiftAsmFile = executeFunc("-W", "dummy.boc", contractPath);
outputFiftAsmFile = StringUtils.replace(outputFiftAsmFile, "2 boc+>B", "0 boc+>B");
File file = new File(new File(contractPath).getParent() + "/dummy.fif");

FileUtils.writeStringToFile(file, outputFiftAsmFile, Charset.defaultCharset());

// output binary boc file
executeFift("-s", file.getAbsolutePath());
byte[] bocContent = FileUtils.readFileToByteArray(new File(file.getParent() + "/dummy.boc"));
return Utils.bytesToHex(bocContent);
}

public String executeFunc(String... params) throws ExecutionException, InterruptedException {
Pair<Process, Future<String>> result = execute("func", params);

return result.getRight().get();
}

public String executeFift(String... params) {
String[] withInclude = new String[]{"-I", fiftAsmLibraryPath + ":" + fiftSmartcontLibraryPath};
String[] all = ArrayUtils.addAll(withInclude, params);
Pair<Process, Future<String>> result = execute("fift", all);
if (nonNull(result)) {
try {
return result.getRight().get();
} catch (Exception e) {
log.info("executeFift error " + e.getMessage());
return null;
}
} else {
return null;
}
}

public Pair<Process, Future<String>> execute(String pathToBinary, String... command) {

String[] withBinaryCommand = new String[]{pathToBinary};

withBinaryCommand = ArrayUtils.addAll(withBinaryCommand, command);

try {
log.info("execute: " + String.join(" ", withBinaryCommand));

ExecutorService executorService = Executors.newSingleThreadExecutor();

final ProcessBuilder pb = new ProcessBuilder(withBinaryCommand).redirectErrorStream(true);

pb.directory(new File(new File(contractPath).getParent()));
Process p = pb.start();

Future<String> future = executorService.submit(() -> {

Thread.currentThread().setName(pathToBinary);

String resultInput = IOUtils.toString(p.getInputStream(), Charset.defaultCharset());

p.getInputStream().close();
p.getErrorStream().close();
p.getOutputStream().close();
if (p.exitValue() != 0) {
log.info("exit value " + p.exitValue());
log.info(resultInput);
throw new Exception("Cannot compile smart-contract.");

}
return resultInput;
});

executorService.shutdown();

return Pair.of(p, future);

} catch (final IOException e) {
log.info(e.getMessage());
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package org.ton.java.smartcontract;


import com.iwebpp.crypto.TweetNaclFast;
import lombok.Builder;
import lombok.Getter;
import org.ton.java.cell.Cell;
import org.ton.java.cell.CellBuilder;
import org.ton.java.smartcontract.wallet.Contract;
import org.ton.java.tlb.types.ExternalMessageInfo;
import org.ton.java.tlb.types.Message;
import org.ton.java.tonlib.Tonlib;
import org.ton.java.tonlib.types.ExtMessageInfo;
import org.ton.java.utils.Utils;

import static java.util.Objects.isNull;

@Builder
@Getter
public class GenericSmartContract implements Contract {

TweetNaclFast.Signature.KeyPair keyPair;
String code;
String data;
private Tonlib tonlib;
private long wc;


@Override
public long getWorkchain() {
return wc;
}

@Override
public String getName() {
return "GenericSmartContract";
}

@Override
public Cell createCodeCell() {
return CellBuilder.beginCell()
.fromBoc(code)
.endCell();
}

@Override
public Cell createDataCell() {
return CellBuilder.beginCell()
.fromBoc(data)
.endCell();
}


public static class GenericSmartContractBuilder {
}

public static GenericSmartContractBuilder builder() {
return new CustomGenericSmartContractBuilder();
}

private static class CustomGenericSmartContractBuilder extends GenericSmartContractBuilder {
@Override
public GenericSmartContract build() {
if (isNull(super.keyPair)) {
super.keyPair = Utils.generateSignatureKeyPair();
}
return super.build();
}
}

public ExtMessageInfo deploy(Cell deployMessageBody) {
return tonlib.sendRawMessage(prepareDeployMsg(deployMessageBody).toCell().toBase64());
}

public Message prepareDeployMsg(Cell deployMessageBody) {

Cell body = deployMessageBody;

return Message.builder()
.info(ExternalMessageInfo.builder()
.dstAddr(getAddressIntStd())
.build())
.init(getStateInit())
.body(CellBuilder.beginCell()
.storeBytes(Utils.signData(keyPair.getPublicKey(), keyPair.getSecretKey(), body.hash()))
.storeCell(body)
.endCell())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ public Cell createDataCell() {
.storeUint(initialSeqno, 32)
.storeUint(walletId, 32)
.storeBytes(keyPair.getPublicKey())
.storeUint(BigInteger.ZERO, 1)//plugins dict empty
.storeUint(0, 1) //plugins dict empty
.endCell();
}

Expand Down
Loading

0 comments on commit 60cbbea

Please sign in to comment.