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}