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

io.neow3j.transaction.Signer Maven / Gradle / Ivy

package io.neow3j.transaction;

import io.neow3j.constants.NeoConstants;
import io.neow3j.crypto.ECKeyPair;
import io.neow3j.serialization.BinaryReader;
import io.neow3j.serialization.BinaryWriter;
import io.neow3j.serialization.IOUtils;
import io.neow3j.serialization.NeoSerializable;
import io.neow3j.serialization.exceptions.DeserializationException;
import io.neow3j.transaction.exceptions.SignerConfigurationException;
import io.neow3j.types.Hash160;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

import static io.neow3j.constants.NeoConstants.MAX_SIGNER_SUBITEMS;

/**
 * A signer of a transaction. It defines a scope in which the signer's signature is valid.
 */
public class Signer extends NeoSerializable {

    /**
     * The script hash of the signer account.
     */
    private Hash160 signerHash;

    /**
     * The scopes in which the signer's signatures can be used. Multiple scopes can be combined.
     */
    private List scopes;

    /**
     * The contract hashes of the contracts that are allowed to use the witness.
     */
    private List allowedContracts;

    /**
     * The group hashes of contracts that are allowed to use the witness.
     */
    private List allowedGroups;

    public Signer() {
    }

    protected Signer(Hash160 signerHash, WitnessScope scope) {
        this.signerHash = signerHash;
        scopes = new ArrayList<>();
        scopes.add(scope);
        allowedContracts = new ArrayList<>();
        allowedGroups = new ArrayList<>();
    }

    /**
     * Adds the given contracts to this signer's scope. These contracts are allowed to use the
     * signers witness.
     *
     * @param allowedContracts the hashes of the allowed contracts.
     * @return this.
     */
    public Signer setAllowedContracts(Hash160... allowedContracts) {
        if (allowedContracts.length == 0) {
            return this;
        }
        if (scopes.contains(WitnessScope.GLOBAL)) {
            throw new SignerConfigurationException("Trying to set allowed contracts on a Signer " +
                    "with global scope.");
        }
        if (this.allowedContracts.size() + allowedContracts.length > MAX_SIGNER_SUBITEMS) {
            throw new SignerConfigurationException("Tyring to set more than " + MAX_SIGNER_SUBITEMS
                    + " allowed contracts on a signer.");
        }
        scopes.remove(WitnessScope.NONE); // remove the none witness scope if it is present.
        scopes.add(WitnessScope.CUSTOM_CONTRACTS);
        this.allowedContracts.addAll(Arrays.asList(allowedContracts));
        return this;
    }

    /**
     * Adds the given contract groups to this signer's scope. The contracts in these groups are
     * allowed to use the signers witness.
     *
     * @param allowedGroups the public keys of the allowed groups.
     * @return this.
     */
    public Signer setAllowedGroups(ECKeyPair.ECPublicKey... allowedGroups) {
        if (allowedGroups.length == 0) {
            return this;
        }
        if (scopes.contains(WitnessScope.GLOBAL)) {
            throw new SignerConfigurationException("Trying to set allowed contract groups on a " +
                    "Signer with global scope.");
        }
        if (this.allowedGroups.size() + allowedGroups.length > MAX_SIGNER_SUBITEMS) {
            throw new SignerConfigurationException("Tyring to set more than " + MAX_SIGNER_SUBITEMS
                    + " allowed contract groups on a signer.");
        }
        scopes.remove(WitnessScope.NONE); // remove the none witness scope if it is present.
        scopes.add(WitnessScope.CUSTOM_GROUPS);
        this.allowedGroups.addAll(Arrays.asList(allowedGroups));
        return this;
    }

    public Hash160 getScriptHash() {
        return signerHash;
    }

    public List getScopes() {
        return scopes;
    }

    public List getAllowedContracts() {
        return allowedContracts;
    }

    public List getAllowedGroups() {
        return allowedGroups;
    }

    @Override
    public void deserialize(BinaryReader reader) throws DeserializationException {
        try {
            signerHash = reader.readSerializable(Hash160.class);
            scopes = WitnessScope.extractCombinedScopes(reader.readByte());
            if (scopes.contains(WitnessScope.CUSTOM_CONTRACTS)) {
                allowedContracts = reader.readSerializableList(Hash160.class);
                if (allowedContracts.size() > MAX_SIGNER_SUBITEMS) {
                    throw new DeserializationException("A signer's scope can only contain "
                            + MAX_SIGNER_SUBITEMS + " allowed contracts. The input data contained "
                            + allowedContracts.size() + " contracts.");
                }
            }
            if (scopes.contains(WitnessScope.CUSTOM_GROUPS)) {
                allowedGroups = reader.readSerializableList(ECKeyPair.ECPublicKey.class);
                if (allowedGroups.size() > MAX_SIGNER_SUBITEMS) {
                    throw new DeserializationException("A signer's scope can only contain "
                            + MAX_SIGNER_SUBITEMS + " allowed contract groups. The input data " +
                            "contained " + allowedGroups.size() + " groups.");
                }
            }
        } catch (IOException e) {
            throw new DeserializationException(e);
        }
    }

    @Override
    public void serialize(BinaryWriter writer) throws IOException {
        writer.writeSerializableFixed(signerHash);
        writer.writeByte(WitnessScope.combineScopes(scopes));
        if (scopes.contains(WitnessScope.CUSTOM_CONTRACTS)) {
            writer.writeSerializableVariable(allowedContracts);
        }
        if (scopes.contains(WitnessScope.CUSTOM_GROUPS)) {
            writer.writeSerializableVariable(allowedGroups);
        }
    }

    @Override
    public int getSize() {
        // Account script hash plus scope byte.
        int size = NeoConstants.HASH160_SIZE + 1;
        if (this.scopes.contains(WitnessScope.CUSTOM_CONTRACTS)) {
            size += IOUtils.getVarSize(this.allowedContracts);
        }
        if (this.scopes.contains(WitnessScope.CUSTOM_GROUPS)) {
            size += IOUtils.getVarSize(this.allowedGroups);
        }
        return size;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        Signer that = (Signer) o;
        return Objects.equals(this.signerHash, that.signerHash) &&
                Objects.equals(this.scopes, that.scopes) &&
                Objects.equals(this.allowedContracts, that.allowedContracts) &&
                Objects.equals(this.allowedGroups, that.allowedGroups);
    }

    @Override
    public int hashCode() {
        return Objects.hash(super.hashCode(), signerHash, scopes, allowedContracts, allowedGroups);
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy