001package org.consensusj.namecoin.jsonrpc.core;
002
003import org.bitcoinj.base.Coin;
004import org.bitcoinj.base.LegacyAddress;
005import org.bitcoinj.base.Monetary;
006import org.bitcoinj.base.Network;
007
008import java.util.ArrayList;
009import java.util.Arrays;
010import java.util.Collections;
011import java.util.HashMap;
012import java.util.List;
013import java.util.Locale;
014import java.util.Map;
015import java.util.Optional;
016import java.util.stream.Stream;
017
018import static org.bitcoinj.base.Coin.COIN;
019
020/**
021 *
022 */
023public enum NameCoinNetwork implements Network {
024    MAINNET("org.namecoin.mainnet"),
025    TESTNET("org.namecoin.testnet"),
026    SIGNET("org.namecoin.signet"),
027    REGTEST("org.namecoin.regtest");
028
029    /**
030     * Scheme part for Bitcoin URIs.
031     */
032    public static final String BITCOIN_SCHEME = "bitcoin";
033
034    /**
035     * The maximum number of coins to be generated
036     */
037    private static final long MAX_COINS = 21_000_000;
038
039    /**
040     * The maximum money to be generated
041     */
042    public static final Coin MAX_MONEY = COIN.multiply(MAX_COINS);
043
044    /** The ID string for the main, production network where people trade things. */
045    public static final String ID_MAINNET = MAINNET.id();
046    /** The ID string for the testnet. */
047    public static final String ID_TESTNET = TESTNET.id();
048    /** The ID string for the signet. */
049    public static final String ID_SIGNET = SIGNET.id();
050    /** The ID string for regtest mode. */
051    public static final String ID_REGTEST = REGTEST.id();
052    /** The ID string for the Unit test network -- there is no corresponding {@code enum}. */
053    public static final String ID_UNITTESTNET = "org.bitcoinj.unittest";
054
055    private final String id;
056
057    // All supported names for this BitcoinNetwork
058    private final List<String> allNames;
059
060    // Maps from names and alternateNames to BitcoinNetwork
061    private static final Map<String, NameCoinNetwork> stringToEnum = mergedNameMap();
062
063    NameCoinNetwork(String networkId, String... alternateNames) {
064        this.id = networkId;
065        this.allNames = combine(this.toString(), alternateNames);
066    }
067
068    /**
069     * Return the canonical, lowercase, user-facing {@code String} for an {@code enum}
070     * @return canonical lowercase value
071     */
072    @Override
073    public String toString() {
074        return name().toLowerCase(Locale.ROOT);
075    }
076
077    /**
078     * Return the network ID string (specified by a {@code Network})
079     *
080     * @return The network ID string
081     */
082    @Override
083    public String id() {
084        return id;
085    }
086
087    /**
088     * Header byte of base58 encoded legacy P2PKH addresses for this network.
089     * @return header byte as an {@code int}.
090     * @see LegacyAddress.AddressHeader
091     */
092    public int legacyAddressHeader() {
093        //return LegacyAddress.AddressHeader.ofNetwork(this).headerByte();
094        return 52;
095    }
096
097    /**
098     * Header byte of base58 encoded legacy P2SH addresses for this network.
099     * @return header byte as an {@code int}.
100     * @see LegacyAddress.P2SHHeader
101     */
102    public int legacyP2SHHeader() {
103        //return LegacyAddress.P2SHHeader.ofNetwork(this).headerByte();
104        return 5;
105    }
106
107    /**
108     * Return the standard Bech32 {@link org.bitcoinj.base.SegwitAddress.SegwitHrp} (as a {@code String}) for
109     * this network.
110     * @return The HRP as a (lowercase) string.
111     */
112    public String segwitAddressHrp() {
113        //return LegacyAddress.P2SHHeader.ofNetwork(this).headerByte();
114        return "??";
115    }
116
117    /**
118     * The URI scheme for Bitcoin.
119     * @see <a href="https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki">BIP 0021</a>
120     * @return string containing the URI scheme
121     */
122    @Override
123    public String uriScheme() {
124        return BITCOIN_SCHEME;
125    }
126
127    @Override
128    public boolean hasMaxMoney() {
129        return true;
130    }
131
132    @Override
133    public Coin maxMoney() {
134        return MAX_MONEY;
135    }
136
137    @Override
138    public boolean exceedsMaxMoney(Monetary amount) {
139        if (amount instanceof Coin) {
140            return ((Coin) amount).compareTo(MAX_MONEY) > 0;
141        } else {
142            throw new IllegalArgumentException("amount must be a Coin type");
143        }
144    }
145
146    /**
147     * Find the {@code BitcoinNetwork} from a name string, e.g. "mainnet", "testnet" or "signet".
148     * A number of common alternate names are allowed too, e.g. "main" or "prod".
149     * @param nameString A name string
150     * @return An {@code Optional} containing the matching enum or empty
151     */
152    public static Optional<NameCoinNetwork> fromString(String nameString) {
153        return Optional.ofNullable(stringToEnum.get(nameString));
154    }
155
156    /**
157     * Find the {@code BitcoinNetwork} from an ID String
158     * <p>
159     * Note: {@link #ID_UNITTESTNET} is not supported as an enum
160     * @param idString specifies the network
161     * @return An {@code Optional} containing the matching enum or empty
162     */
163    public static Optional<NameCoinNetwork> fromIdString(String idString) {
164        return Arrays.stream(values())
165                .filter(n -> n.id.equals(idString))
166                .findFirst();
167    }
168
169    // Create a Map that maps name Strings to networks for all instances
170    private static Map<String, NameCoinNetwork> mergedNameMap() {
171        return Stream.of(values())
172                .collect(HashMap::new,                  // Supply HashMaps as mutable containers
173                        NameCoinNetwork::accumulateNames,    // Accumulate one network into hashmap
174                        Map::putAll);                       // Combine two containers
175    }
176
177    // Add allNames for this Network as keys to a map that can be used to find it
178    private static void accumulateNames(Map<String, NameCoinNetwork> map, NameCoinNetwork net) {
179        net.allNames.forEach(name -> map.put(name, net));
180    }
181
182    // Combine a String and an array of String and return as an unmodifiable list
183    private static List<String> combine(String canonical, String[] alternateNames) {
184        List<String> temp = new ArrayList<>();
185        temp.add(canonical);
186        temp.addAll(Arrays.asList(alternateNames));
187        return Collections.unmodifiableList(temp);
188    }
189
190}