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.contract.ScriptHash;
import io.neow3j.crypto.ECKeyPair;
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.transaction.exceptions.SignerConfigurationException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;


/**
 * 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 ScriptHash account;

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

    /**
     * The script hashes of 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() {
        this.account = new ScriptHash();
        this.scopes = new ArrayList<>();
        this.allowedContracts = new ArrayList<>();
        this.allowedGroups = new ArrayList<>();
    }

    private Signer(Builder builder) {
        this.account = builder.account;
        this.scopes = builder.scopes;
        this.allowedContracts = builder.allowedContracts;
        this.allowedGroups = builder.allowedGroups;
    }

    /**
     * Creates a Signer for the given account with fee only witness scope ({@link
     * WitnessScope#FEE_ONLY}).
     *
     * @param account The originator of the witness.
     * @return {@link Signer}
     */
    public static Signer feeOnly(ScriptHash account) {
        return new Builder()
                .account(account)
                .scopes(WitnessScope.FEE_ONLY)
                .build();
    }

    /**
     * Creates a Signer for the given account with the most restrictive witness scope ({@link
     * WitnessScope#CALLED_BY_ENTRY}).
     *
     * @param account The originator of the witness.
     * @return {@link Signer}
     */
    public static Signer calledByEntry(ScriptHash account) {
        return new Builder()
                .account(account)
                .scopes(WitnessScope.CALLED_BY_ENTRY)
                .build();
    }

    /**
     * Creates a Signer for the given account with global witness scope ({@link
     * WitnessScope#GLOBAL}).
     *
     * @param account The originator of the witness.
     * @return {@link Signer}
     */
    public static Signer global(ScriptHash account) {
        return new Builder()
                .account(account)
                .scopes(WitnessScope.GLOBAL)
                .build();
    }

    public ScriptHash getScriptHash() {
        return account;
    }

    public List getScopes() {
        return scopes;
    }

    public List getAllowedContracts() {
        return allowedContracts;
    }

    public List getAllowedGroups() {
        return allowedGroups;
    }

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

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

    @Override
    public int getSize() {
        // Account script hash plus scope byte.
        int size = NeoConstants.SCRIPTHASH_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.account, that.account) &&
                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(), account, scopes, allowedContracts, allowedGroups);
    }

    public static class Builder {

        private ScriptHash account;
        private List scopes;
        private List allowedContracts;
        private List allowedGroups;

        public Builder() {
            this.scopes = new ArrayList<>();
            this.allowedContracts = new ArrayList<>();
            this.allowedGroups = new ArrayList<>();
        }

        /**
         * Sets the account for which this Signer object specifies the witness scopes.
         *
         * @param account the account's script hash
         * @return this builder.
         */
        public Builder account(ScriptHash account) {
            this.account = account;
            return this;
        }

        /**
         * Sets the witness scopes in which the witness may be used.
         * 

* Note that the global ({@link WitnessScope#GLOBAL}) scope cannot be mixed with any other * scopes. * * @param scopes one or more witness scopes. * @return this builder. */ public Builder scopes(WitnessScope... scopes) { this.scopes.addAll(Arrays.asList(scopes)); return this; } /** * Sets the contracts that are allowed to use the witness. *

* When adding contracts here the {@link WitnessScope#CUSTOM_CONTRACTS} scope is added * automatically. * * @param contracts one or more contract script hashes. * @return this builder. */ public Builder allowedContracts(ScriptHash... contracts) { if (!this.scopes.contains(WitnessScope.CUSTOM_CONTRACTS)) { this.scopes.add(WitnessScope.CUSTOM_CONTRACTS); } if (this.allowedContracts.size() + contracts.length > NeoConstants.MAX_SIGNER_SUBITEMS) { throw new SignerConfigurationException("A signer's scope can only contain " + NeoConstants.MAX_SIGNER_SUBITEMS + " contracts."); } this.allowedContracts.addAll(Arrays.asList(contracts)); return this; } /** * Sets the the groups that are allowed to use the witness. *

* When adding groups here the {@link WitnessScope#CUSTOM_GROUPS} scope is added * automatically. * * @param groups one or more group public keys as elliptic curve points. * @return this builder. */ public Builder allowedGroups(ECKeyPair.ECPublicKey... groups) { if (!this.scopes.contains(WitnessScope.CUSTOM_GROUPS)) { this.scopes.add(WitnessScope.CUSTOM_GROUPS); } if (this.allowedGroups.size() + groups.length > NeoConstants.MAX_SIGNER_SUBITEMS) { throw new SignerConfigurationException("A signer's scope can only contain " + NeoConstants.MAX_SIGNER_SUBITEMS + " groups."); } this.allowedGroups.addAll(Arrays.asList(groups)); return this; } /** * Builds the signer. * * @return the signer. * @throws SignerConfigurationException if either *

    *
  • no account has been set
  • *
  • no scope has been set
  • *
  • the global scope is mixed with other * scopes
  • *
  • the custom contracts scope is set but * no contracts are specified
  • *
  • the custom groups scope is set but * no groups are specified
  • *
*/ public Signer build() { if (account == null) { throw new SignerConfigurationException("No account has been set. A signer" + " object requires an account."); } if (scopes.isEmpty()) { throw new SignerConfigurationException("No scope has been defined. A signer" + " object requires at least one scope."); } if (scopes.contains(WitnessScope.FEE_ONLY) && scopes.size() > 1) { throw new SignerConfigurationException("The fee-only witness scope cannot be " + "combined with other scopes."); } if (scopes.contains(WitnessScope.GLOBAL) && scopes.size() > 1) { throw new SignerConfigurationException("The global witness scope cannot be " + "combined with other scopes."); } if (scopes.contains(WitnessScope.CUSTOM_CONTRACTS) && allowedContracts.isEmpty()) { throw new SignerConfigurationException("Set of allowed contracts must not be " + "empty for a signer with the custom contracts scope."); } if (scopes.contains(WitnessScope.CUSTOM_GROUPS) && allowedGroups.isEmpty()) { throw new SignerConfigurationException("Set of allowed groups must not be " + "empty for a signer with the custom groups scope."); } return new Signer(this); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy