All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.neow3j.contract.NefFile Maven / Gradle / Ivy

package io.neow3j.contract;

import io.neow3j.constants.NeoConstants;
import io.neow3j.crypto.Hash;
import io.neow3j.io.BinaryReader;
import io.neow3j.io.BinaryWriter;
import io.neow3j.io.IOUtils;
import io.neow3j.io.NeoSerializable;
import io.neow3j.io.exceptions.DeserializationException;
import io.neow3j.utils.ArrayUtils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.Objects;

/**
 * +------------+-----------+------------------------------------------------------------+
 * |   Field    |  Length   |                          Comment                           |
 * +------------+-----------+------------------------------------------------------------+
 * | Magic      | 4 bytes   | Magic header                                               |
 * | Compiler   | 32 bytes  | Compiler used                                              |
 * | Version    | 16 bytes  | Compiler version (Mayor, Minor, Build, Version)            |
 * | ScriptHash | 20 bytes  | ScriptHash for the script                                  |
 * +------------+-----------+------------------------------------------------------------+
 * | Checksum   | 4 bytes   | Sha256 of the header (CRC)                                 |
 * +------------+-----------+------------------------------------------------------------+
 * | Script     | Var bytes | Var bytes for the payload                                  |
 * +------------+-----------+------------------------------------------------------------+
 */
public class NefFile extends NeoSerializable {

    // NEO Executable Format 3 (NEF3)
    private static final int MAGIC = 0x3346454E; // 860243278 in decimal

    private static final int HEADER_SIZE = 4    // Magic (uint32)
            + 32                                // Compiler (32 bytes String)
            + (4 * 4)                           // Version (4 * int32)
            + NeoConstants.SCRIPTHASH_SIZE      // Script hash
            + 4;                                // Checksum

    private String compiler;
    private Version version;
    public ScriptHash scriptHash;
    public byte[] checkSum; // 4 bytes. A uint in neo-core
    public byte[] script;

    public NefFile() {
        this.checkSum = new byte[]{};
        this.script = new byte[]{};
    }

    public NefFile(String compiler, Version version, byte[] script) {
        this.compiler = compiler;
        this.version = version;
        this.script = script;
        this.scriptHash = ScriptHash.fromScript(script);
        // Need to initialize the check sum because it is required for calculating the check sum.
        this.checkSum = new byte[]{};
        this.checkSum = computeChecksum(this);
    }

    public String getCompiler() {
        return compiler;
    }

    public Version getVersion() {
        return version;
    }

    public ScriptHash getScriptHash() {
        return scriptHash;
    }

    public byte[] getCheckSum() {
        return checkSum;
    }

    public byte[] getScript() {
        return script;
    }

    @Override
    public int getSize() {
        return HEADER_SIZE + IOUtils.getVarSize(this.script);
    }

    @Override
    public void serialize(BinaryWriter writer) throws IOException {
        writer.writeUInt32(MAGIC);
        writer.writeFixedString(this.compiler, 32);
        writer.writeSerializableFixed(this.version);
        writer.writeSerializableFixed(this.scriptHash);
        writer.write(this.checkSum);
        writer.writeVarBytes(this.script);
    }

    @Override
    public void deserialize(BinaryReader reader) throws DeserializationException {
        try {
            long l = reader.readUInt32();
            if (l != MAGIC) {
                throw new DeserializationException("Wrong magic number in NEF file.");
            }
            byte[] compilerBytes = ArrayUtils.trimTrailingBytes(reader.readBytes(32), (byte)0);
            this.compiler = new String(compilerBytes, StandardCharsets.UTF_8);
            this.version = reader.readSerializable(Version.class);
            this.scriptHash = reader.readSerializable(ScriptHash.class);
            this.checkSum = reader.readBytes(4);
            if (!Arrays.equals(this.checkSum, computeChecksum(this))) {
                throw new DeserializationException("The checksums did not match");
            }
            this.script = reader.readVarBytes(1024 * 1024);
        } catch (IOException e) {
            throw new DeserializationException(e);
        }
    }

    public static byte[] computeChecksum(NefFile file) {
        byte[] serialized = new byte[]{};
        try (ByteArrayOutputStream stream = new ByteArrayOutputStream()) {
            BinaryWriter writer = new BinaryWriter(stream);
            writer.writeSerializableFixed(file);
            serialized = stream.toByteArray();
        } catch (IOException e) {
            // Doesn't happen because we're not writing to anywhere.
        }
        // Get header without the checksum.
        byte[] header = ArrayUtils.getFirstNBytes(serialized, HEADER_SIZE - 4);
        return ArrayUtils.getFirstNBytes(Hash.hash256(header), 4);
    }

    public static NefFile readFromFile(File nefFile) throws DeserializationException, IOException {
        int nefFileSize = (int) nefFile.length();
        if (nefFileSize > 0x100000) {
            // This maximum size was taken from the neo-core code.
            throw new IllegalArgumentException("The given NEF file is too large. File was "
                    + nefFileSize + " bytes, but a max of 2^20 bytes is allowed.");
        }
        try (FileInputStream nefStream = new FileInputStream(nefFile)) {
            BinaryReader reader = new BinaryReader(nefStream);
            return reader.readSerializable(NefFile.class);
        }
    }

    public static class Version extends NeoSerializable {

        private int major;
        private int minor;
        private int build;
        private int revision;

        public Version() {

        }

        public Version(int major, int minor, int build, int revision) {
            this.major = major;
            this.minor = minor;
            this.build = build;
            this.revision = revision;
        }

        public int getMajor() {
            return major;
        }

        public int getMinor() {
            return minor;
        }

        public int getBuild() {
            return build;
        }

        public int getRevision() {
            return revision;
        }

        @Override
        public void deserialize(BinaryReader reader) throws DeserializationException {
            try {
                this.major = reader.readInt();
                this.minor = reader.readInt();
                this.build = reader.readInt();
                this.revision = reader.readInt();
            } catch (IOException e) {
                throw new DeserializationException(e);
            }
        }

        @Override
        public void serialize(BinaryWriter writer) throws IOException {
            writer.writeInt32(this.major);
            writer.writeInt32(this.minor);
            writer.writeInt32(this.build);
            writer.writeInt32(this.revision);
        }

        @Override
        public int getSize() {
            return 32 * 4;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (!(o instanceof Version)) {
                return false;
            }
            Version version = (Version) o;
            return major == version.major
                    && minor == version.minor
                    && build == version.build
                    && revision == version.revision;
        }

        @Override
        public int hashCode() {
            return Objects.hash(major, minor, build, revision);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy