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

multiversx.Address Maven / Gradle / Ivy

package multiversx;

import java.io.ByteArrayOutputStream;

import org.bitcoinj.core.Bech32;
import org.bouncycastle.util.encoders.DecoderException;
import org.bouncycastle.util.encoders.Hex;

import multiversx.Exceptions.AddressException;

public class Address {
    static final String HRP = "erd";
    static final int PUBKEY_LENGTH = 32;
    static final int PUBKEY_STRING_LENGTH = PUBKEY_LENGTH * 2; // hex-encoded
    static final int BECH32_LENGTH = 62;
    static final String ZERO_PUBKEY_STRING = "0000000000000000000000000000000000000000000000000000000000000000";

    private final String valueHex;

    private Address(String valueHex) {
        this.valueHex = valueHex;
    }

    public static Address createZeroAddress() {
        return new Address(ZERO_PUBKEY_STRING);
    }

    public static Address fromBech32(String value) throws Exceptions.AddressException {
        Bech32.Bech32Data bech32Data;
        try {
            bech32Data = Bech32.decode(value);
        }
        catch(Exception e) {
            throw new Exceptions.CannotCreateBech32AddressException(value);
        }
        if (!bech32Data.hrp.equals(HRP)) {
            throw new Exceptions.BadAddressHrpException();
        }

        byte[] decodedBytes = convertBits(bech32Data.data, 5, 8, false);
        String hex = new String(Hex.encode(decodedBytes));
        return new Address(hex);
    }

    public static Address fromHex(String value) throws Exceptions.AddressException {
        if (value.length() != PUBKEY_STRING_LENGTH || !isValidHex(value)) {
            throw new Exceptions.CannotCreateAddressException(value);
        }

        return new Address(value);
    }

    private static boolean isValidHex(String value) {
        try {
            Hex.decode(value);
            return true;
        } catch (DecoderException error) {
            return false;
        }
    }

    public String hex() {
        return this.valueHex;
    }

    public byte[] pubkey() {
        return Hex.decode(this.valueHex);
    }

    public String bech32() throws AddressException {
        byte[] pubkey = this.pubkey();
        return Bech32.encode(HRP, convertBits(pubkey, 8, 5, true));
    }

    public static boolean isValidBech32(String value) {
        try {
            Address.fromBech32(value);
            return true;
        } catch (AddressException error) {
            return false;
        }
    }

    /**
     * @param data represents the bytes to be converted
     * @param fromBits represents the starting position
     * @param toBits represents the ending position
     * @param pad if set to true, will pad the result
     * @return returns the converted bytes
     * @throws AddressException if there is an issue with the data
     * General power-of-2 base conversion.
     */
    public static byte[] convertBits(byte[] data, int fromBits, int toBits, boolean pad) throws Exceptions.AddressException {
        /*-
        Reference Python implementation by Pieter Wuille:
        
        def convertbits(data, frombits, tobits, pad=True):
            acc = 0
            bits = 0
            ret = []
            maxv = (1 << tobits) - 1
            max_acc = (1 << (frombits + tobits - 1)) - 1
            for value in data:
                if value < 0 or (value >> frombits):
                    return None
                acc = ((acc << frombits) | value) & max_acc
                bits += frombits
                while bits >= tobits:
                    bits -= tobits
                    ret.append((acc >> bits) & maxv)
            if pad:
                if bits:
                    ret.append((acc << (tobits - bits)) & maxv)
            elif bits >= frombits or ((acc << (tobits - bits)) & maxv):
                return None
            return ret
        */
        int acc = 0;
        int bits = 0;
        ByteArrayOutputStream ret = new ByteArrayOutputStream();
        int maxv = (1 << toBits) - 1;
        int maxAcc = (1 << (fromBits + toBits - 1)) - 1;

        for (byte value : data) {
            int valueAsInt = value & 0xff;

            if ((valueAsInt < 0) || (valueAsInt >>> fromBits != 0)) {
                throw new Exceptions.CannotConvertBitsException();
            }

            acc = ((acc << fromBits) | valueAsInt) & maxAcc;
            bits += fromBits;

            while (bits >= toBits) {
                bits -= toBits;
                ret.write((acc >>> bits) & maxv);
            }
        }

        if (pad) {
            if (bits > 0) {
                ret.write((acc << (toBits - bits)) & maxv);
            }
        } else if (bits >= fromBits || ((acc << (toBits - bits)) & maxv) != 0) {
            throw new Exceptions.CannotConvertBitsException();
        }

        return ret.toByteArray();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy