Networking code, untested
This commit is contained in:
60
domain/src/main/java/ch/dissem/bitmessage/Context.java
Normal file
60
domain/src/main/java/ch/dissem/bitmessage/Context.java
Normal file
@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage;
|
||||
|
||||
import ch.dissem.bitmessage.ports.AddressRepository;
|
||||
import ch.dissem.bitmessage.ports.Inventory;
|
||||
import ch.dissem.bitmessage.ports.NetworkMessageReceiver;
|
||||
import ch.dissem.bitmessage.ports.NetworkMessageSender;
|
||||
|
||||
/**
|
||||
* Created by chris on 05.04.15.
|
||||
*/
|
||||
public class Context {
|
||||
public static final int CURRENT_VERSION = 3;
|
||||
|
||||
private static Context instance;
|
||||
|
||||
private Inventory inventory;
|
||||
private AddressRepository addressRepo;
|
||||
private NetworkMessageSender sender;
|
||||
private NetworkMessageReceiver receiver;
|
||||
|
||||
private Context(Inventory inventory, AddressRepository addressRepo,
|
||||
NetworkMessageSender sender, NetworkMessageReceiver receiver) {
|
||||
this.inventory = inventory;
|
||||
this.addressRepo = addressRepo;
|
||||
this.sender = sender;
|
||||
this.receiver = receiver;
|
||||
}
|
||||
|
||||
public static void init(Inventory inventory, AddressRepository addressRepository, NetworkMessageSender sender, NetworkMessageReceiver receiver) {
|
||||
instance = new Context(inventory, addressRepository, sender, receiver);
|
||||
}
|
||||
|
||||
public static Context getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public Inventory getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
public AddressRepository getAddressRepository() {
|
||||
return addressRepo;
|
||||
}
|
||||
}
|
@ -22,12 +22,13 @@ import ch.dissem.bitmessage.utils.Encode;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* The 'addr' command holds a list of known active Bitmessage nodes.
|
||||
*/
|
||||
public class Addr implements Command {
|
||||
public class Addr implements MessagePayload {
|
||||
private final List<NetworkAddress> addresses;
|
||||
|
||||
private Addr(Builder builder) {
|
||||
@ -35,8 +36,12 @@ public class Addr implements Command {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommand() {
|
||||
return "addr";
|
||||
public Command getCommand() {
|
||||
return Command.ADDR;
|
||||
}
|
||||
|
||||
public List<NetworkAddress> getAddresses() {
|
||||
return addresses;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -53,6 +58,11 @@ public class Addr implements Command {
|
||||
public Builder() {
|
||||
}
|
||||
|
||||
public Builder addresses(Collection<NetworkAddress> addresses){
|
||||
this.addresses.addAll(addresses);
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder addAddress(final NetworkAddress address) {
|
||||
this.addresses.add(address);
|
||||
return this;
|
||||
|
@ -27,7 +27,7 @@ import java.util.List;
|
||||
/**
|
||||
* The 'getdata' command is used to request objects from a node.
|
||||
*/
|
||||
public class GetData implements Command {
|
||||
public class GetData implements MessagePayload {
|
||||
List<InventoryVector> inventory;
|
||||
|
||||
private GetData(Builder builder) {
|
||||
@ -35,8 +35,12 @@ public class GetData implements Command {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommand() {
|
||||
return "getdata";
|
||||
public Command getCommand() {
|
||||
return Command.GETDATA;
|
||||
}
|
||||
|
||||
public List<InventoryVector> getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -27,16 +27,20 @@ import java.util.List;
|
||||
/**
|
||||
* The 'inv' command holds up to 50000 inventory vectors, i.e. hashes of inventory items.
|
||||
*/
|
||||
public class Inv implements Command {
|
||||
public class Inv implements MessagePayload {
|
||||
private List<InventoryVector> inventory;
|
||||
|
||||
private Inv(Builder builder) {
|
||||
inventory = builder.inventory;
|
||||
}
|
||||
|
||||
public List<InventoryVector> getInventory() {
|
||||
return inventory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommand() {
|
||||
return "inv";
|
||||
public Command getCommand() {
|
||||
return Command.INV;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -19,6 +19,10 @@ package ch.dissem.bitmessage.entity;
|
||||
/**
|
||||
* A command can hold a network message payload
|
||||
*/
|
||||
public interface Command extends Streamable {
|
||||
String getCommand();
|
||||
public interface MessagePayload extends Streamable {
|
||||
Command getCommand();
|
||||
|
||||
enum Command {
|
||||
VERSION, VERACK, ADDR, INV, GETDATA, OBJECT
|
||||
}
|
||||
}
|
@ -16,7 +16,6 @@
|
||||
|
||||
package ch.dissem.bitmessage.entity;
|
||||
|
||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
||||
import ch.dissem.bitmessage.utils.Encode;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@ -39,12 +38,9 @@ public class NetworkMessage implements Streamable {
|
||||
public final static int MAGIC = 0xE9BEB4D9;
|
||||
public final static byte[] MAGIC_BYTES = ByteBuffer.allocate(4).putInt(MAGIC).array();
|
||||
|
||||
private final NetworkAddress targetNode;
|
||||
private final MessagePayload payload;
|
||||
|
||||
private final Command payload;
|
||||
|
||||
public NetworkMessage(NetworkAddress target, Command payload) {
|
||||
this.targetNode = target;
|
||||
public NetworkMessage(MessagePayload payload) {
|
||||
this.payload = payload;
|
||||
}
|
||||
|
||||
@ -59,22 +55,19 @@ public class NetworkMessage implements Streamable {
|
||||
/**
|
||||
* The actual data, a message or an object. Not to be confused with objectPayload.
|
||||
*/
|
||||
public Command getPayload() {
|
||||
public MessagePayload getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public NetworkAddress getTargetNode() {
|
||||
return targetNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(OutputStream stream) throws IOException {
|
||||
// magic
|
||||
Encode.int32(MAGIC, stream);
|
||||
|
||||
// ASCII string identifying the packet content, NULL padded (non-NULL padding results in packet rejected)
|
||||
stream.write(payload.getCommand().getBytes("ASCII"));
|
||||
for (int i = payload.getCommand().length(); i < 12; i++) {
|
||||
String command = payload.getCommand().name().toLowerCase();
|
||||
stream.write(command.getBytes("ASCII"));
|
||||
for (int i = command.length(); i < 12; i++) {
|
||||
stream.write('\0');
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,7 @@
|
||||
package ch.dissem.bitmessage.entity;
|
||||
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||
import ch.dissem.bitmessage.utils.Encode;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -25,7 +26,7 @@ import java.io.OutputStream;
|
||||
/**
|
||||
* The 'object' command sends an object that is shared throughout the network.
|
||||
*/
|
||||
public class ObjectMessage implements Command {
|
||||
public class ObjectMessage implements MessagePayload {
|
||||
private long nonce;
|
||||
private long expiresTime;
|
||||
private long objectType;
|
||||
@ -47,8 +48,17 @@ public class ObjectMessage implements Command {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommand() {
|
||||
return "object";
|
||||
public Command getCommand() {
|
||||
return Command.OBJECT;
|
||||
}
|
||||
|
||||
public ObjectPayload getPayload() {
|
||||
return payload;
|
||||
}
|
||||
|
||||
public InventoryVector getInventoryVector() {
|
||||
// TODO
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -22,10 +22,10 @@ import java.io.OutputStream;
|
||||
/**
|
||||
* The 'verack' command answers a 'version' command, accepting the other node's version.
|
||||
*/
|
||||
public class VerAck implements Command {
|
||||
public class VerAck implements MessagePayload {
|
||||
@Override
|
||||
public String getCommand() {
|
||||
return "verack";
|
||||
public Command getCommand() {
|
||||
return Command.VERACK;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -16,6 +16,7 @@
|
||||
|
||||
package ch.dissem.bitmessage.entity;
|
||||
|
||||
import ch.dissem.bitmessage.Context;
|
||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
||||
import ch.dissem.bitmessage.utils.Encode;
|
||||
|
||||
@ -26,8 +27,7 @@ import java.util.Random;
|
||||
/**
|
||||
* The 'version' command advertises this node's latest supported protocol version upon initiation.
|
||||
*/
|
||||
public class Version implements Command {
|
||||
public static final int CURRENT = 3;
|
||||
public class Version implements MessagePayload {
|
||||
/**
|
||||
* Identifies protocol version being used by the node. Should equal 3. Nodes should disconnect if the remote node's
|
||||
* version is lower but continue with the connection if it is higher.
|
||||
@ -71,6 +71,17 @@ public class Version implements Command {
|
||||
*/
|
||||
private final long[] streamNumbers;
|
||||
|
||||
private Version(Builder builder) {
|
||||
version = builder.version;
|
||||
services = builder.services;
|
||||
timestamp = builder.timestamp;
|
||||
addrRecv = builder.addrRecv;
|
||||
addrFrom = builder.addrFrom;
|
||||
nonce = builder.nonce;
|
||||
userAgent = builder.userAgent;
|
||||
streamNumbers = builder.streamNumbers;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
@ -99,24 +110,13 @@ public class Version implements Command {
|
||||
return userAgent;
|
||||
}
|
||||
|
||||
public long[] getStreamNumbers() {
|
||||
public long[] getStreams() {
|
||||
return streamNumbers;
|
||||
}
|
||||
|
||||
private Version(Builder builder) {
|
||||
version = builder.version;
|
||||
services = builder.services;
|
||||
timestamp = builder.timestamp;
|
||||
addrRecv = builder.addrRecv;
|
||||
addrFrom = builder.addrFrom;
|
||||
nonce = builder.nonce;
|
||||
userAgent = builder.userAgent;
|
||||
streamNumbers = builder.streamNumbers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCommand() {
|
||||
return "version";
|
||||
public Command getCommand() {
|
||||
return Command.VERSION;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -146,7 +146,7 @@ public class Version implements Command {
|
||||
}
|
||||
|
||||
public Builder defaults() {
|
||||
version = CURRENT;
|
||||
version = Context.CURRENT;
|
||||
services = 1;
|
||||
timestamp = System.currentTimeMillis() / 1000;
|
||||
nonce = new Random().nextInt();
|
||||
|
@ -40,7 +40,7 @@ class V3MessageFactory {
|
||||
byte[] payloadBytes = Decode.bytes(stream, length);
|
||||
|
||||
if (testChecksum(checksum, payloadBytes)) {
|
||||
Command payload = getPayload(command, new ByteArrayInputStream(payloadBytes), length);
|
||||
MessagePayload payload = getPayload(command, new ByteArrayInputStream(payloadBytes), length);
|
||||
return new NetworkMessage(payload);
|
||||
} else {
|
||||
throw new IOException("Checksum failed for message '" + command + "'");
|
||||
@ -49,7 +49,7 @@ class V3MessageFactory {
|
||||
return null;
|
||||
}
|
||||
|
||||
private Command getPayload(String command, InputStream stream, int length) throws IOException {
|
||||
private MessagePayload getPayload(String command, InputStream stream, int length) throws IOException {
|
||||
switch (command) {
|
||||
case "version":
|
||||
return parseVersion(stream);
|
||||
|
@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.ports;
|
||||
|
||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Stores and provides known peers.
|
||||
*/
|
||||
public interface AddressRepository {
|
||||
List<NetworkAddress> getKnownAddresses(int limit, long... streams);
|
||||
|
||||
void offerAddresses(List<NetworkAddress> addresses);
|
||||
}
|
@ -16,6 +16,7 @@
|
||||
|
||||
package ch.dissem.bitmessage.ports;
|
||||
|
||||
import ch.dissem.bitmessage.entity.ObjectMessage;
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
||||
import ch.dissem.bitmessage.entity.valueobject.InventoryVector;
|
||||
|
||||
@ -25,13 +26,13 @@ import java.util.List;
|
||||
* The Inventory stores and retrieves objects, cleans up outdated objects and can tell which objects are still missing.
|
||||
*/
|
||||
public interface Inventory {
|
||||
public List<InventoryVector> getInventory();
|
||||
public List<InventoryVector> getInventory(long... streams);
|
||||
|
||||
public List<InventoryVector> getMissing(List<InventoryVector> offer);
|
||||
|
||||
public ObjectPayload getObject(InventoryVector vector);
|
||||
public ObjectMessage getObject(InventoryVector vector);
|
||||
|
||||
public void storeObject(InventoryVector vector, ObjectPayload object);
|
||||
public void storeObject(ObjectMessage object);
|
||||
|
||||
public void cleanup();
|
||||
}
|
||||
|
@ -17,6 +17,7 @@
|
||||
package ch.dissem.bitmessage.ports;
|
||||
|
||||
import ch.dissem.bitmessage.entity.NetworkMessage;
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -25,11 +26,11 @@ import java.io.IOException;
|
||||
* Handles incoming messages
|
||||
*/
|
||||
public interface NetworkMessageReceiver {
|
||||
public void registerListener(int port) throws IOException;
|
||||
void registerListener(int port, MessageListener listener) throws IOException;
|
||||
|
||||
public void registerListener(NetworkAddress node, MessageListener listener) throws IOException;
|
||||
void registerListener(NetworkAddress node, MessageListener listener) throws IOException;
|
||||
|
||||
public static interface MessageListener {
|
||||
public void receive(NetworkMessage message);
|
||||
interface MessageListener {
|
||||
void receive(ObjectPayload payload);
|
||||
}
|
||||
}
|
||||
|
@ -16,12 +16,12 @@
|
||||
|
||||
package ch.dissem.bitmessage.ports;
|
||||
|
||||
import ch.dissem.bitmessage.entity.NetworkMessage;
|
||||
import ch.dissem.bitmessage.entity.payload.ObjectPayload;
|
||||
import ch.dissem.bitmessage.entity.valueobject.NetworkAddress;
|
||||
|
||||
/**
|
||||
* Sends messages
|
||||
*/
|
||||
public interface NetworkMessageSender {
|
||||
public void send(NetworkAddress node, NetworkMessage message);
|
||||
void send(NetworkAddress node, ObjectPayload payload);
|
||||
}
|
||||
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ch.dissem.bitmessage.utils;
|
||||
|
||||
/**
|
||||
* Indicates an illegal Bitmessage address
|
||||
*/
|
||||
public class AddressFormatException extends RuntimeException {
|
||||
public AddressFormatException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@ -1,11 +1,12 @@
|
||||
/*
|
||||
* Copyright 2011 Google Inc.
|
||||
* Copyright 2015 Christian Basler
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@ -16,17 +17,151 @@
|
||||
|
||||
package ch.dissem.bitmessage.utils;
|
||||
|
||||
import java.io.UnsupportedEncodingException;
|
||||
|
||||
/**
|
||||
* Base58 encoder and decoder
|
||||
*/
|
||||
public class Base58 {
|
||||
private static char[] ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz".toCharArray();
|
||||
|
||||
public static String encode(byte[] input) {
|
||||
return null; // TODO
|
||||
private static final int[] INDEXES = new int[128];
|
||||
|
||||
static {
|
||||
for (int i = 0; i < INDEXES.length; i++) {
|
||||
INDEXES[i] = -1;
|
||||
}
|
||||
for (int i = 0; i < ALPHABET.length; i++) {
|
||||
INDEXES[ALPHABET[i]] = i;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] decode(String input) {
|
||||
return null; // TODO
|
||||
/**
|
||||
* Encodes the given bytes in base58. No checksum is appended.
|
||||
*/
|
||||
public static String encode(byte[] input) {
|
||||
if (input.length == 0) {
|
||||
return "";
|
||||
}
|
||||
input = copyOfRange(input, 0, input.length);
|
||||
// Count leading zeroes.
|
||||
int zeroCount = 0;
|
||||
while (zeroCount < input.length && input[zeroCount] == 0) {
|
||||
++zeroCount;
|
||||
}
|
||||
// The actual encoding.
|
||||
byte[] temp = new byte[input.length * 2];
|
||||
int j = temp.length;
|
||||
|
||||
int startAt = zeroCount;
|
||||
while (startAt < input.length) {
|
||||
byte mod = divmod58(input, startAt);
|
||||
if (input[startAt] == 0) {
|
||||
++startAt;
|
||||
}
|
||||
temp[--j] = (byte) ALPHABET[mod];
|
||||
}
|
||||
|
||||
// Strip extra '1' if there are some after decoding.
|
||||
while (j < temp.length && temp[j] == ALPHABET[0]) {
|
||||
++j;
|
||||
}
|
||||
// Add as many leading '1' as there were leading zeros.
|
||||
while (--zeroCount >= 0) {
|
||||
temp[--j] = (byte) ALPHABET[0];
|
||||
}
|
||||
|
||||
byte[] output = copyOfRange(temp, j, temp.length);
|
||||
try {
|
||||
return new String(output, "US-ASCII");
|
||||
} catch (UnsupportedEncodingException e) {
|
||||
throw new RuntimeException(e); // Cannot happen.
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] decode(String input) throws AddressFormatException {
|
||||
if (input.length() == 0) {
|
||||
return new byte[0];
|
||||
}
|
||||
byte[] input58 = new byte[input.length()];
|
||||
// Transform the String to a base58 byte sequence
|
||||
for (int i = 0; i < input.length(); ++i) {
|
||||
char c = input.charAt(i);
|
||||
|
||||
int digit58 = -1;
|
||||
if (c >= 0 && c < 128) {
|
||||
digit58 = INDEXES[c];
|
||||
}
|
||||
if (digit58 < 0) {
|
||||
throw new AddressFormatException("Illegal character " + c + " at " + i);
|
||||
}
|
||||
|
||||
input58[i] = (byte) digit58;
|
||||
}
|
||||
// Count leading zeroes
|
||||
int zeroCount = 0;
|
||||
while (zeroCount < input58.length && input58[zeroCount] == 0) {
|
||||
++zeroCount;
|
||||
}
|
||||
// The encoding
|
||||
byte[] temp = new byte[input.length()];
|
||||
int j = temp.length;
|
||||
|
||||
int startAt = zeroCount;
|
||||
while (startAt < input58.length) {
|
||||
byte mod = divmod256(input58, startAt);
|
||||
if (input58[startAt] == 0) {
|
||||
++startAt;
|
||||
}
|
||||
|
||||
temp[--j] = mod;
|
||||
}
|
||||
// Do no add extra leading zeroes, move j to first non null byte.
|
||||
while (j < temp.length && temp[j] == 0) {
|
||||
++j;
|
||||
}
|
||||
|
||||
return copyOfRange(temp, j - zeroCount, temp.length);
|
||||
}
|
||||
|
||||
//
|
||||
// number -> number / 58, returns number % 58
|
||||
//
|
||||
private static byte divmod58(byte[] number, int startAt) {
|
||||
int remainder = 0;
|
||||
for (int i = startAt; i < number.length; i++) {
|
||||
int digit256 = (int) number[i] & 0xFF;
|
||||
int temp = remainder * 256 + digit256;
|
||||
|
||||
number[i] = (byte) (temp / 58);
|
||||
|
||||
remainder = temp % 58;
|
||||
}
|
||||
|
||||
return (byte) remainder;
|
||||
}
|
||||
|
||||
//
|
||||
// number -> number / 256, returns number % 256
|
||||
//
|
||||
private static byte divmod256(byte[] number58, int startAt) {
|
||||
int remainder = 0;
|
||||
for (int i = startAt; i < number58.length; i++) {
|
||||
int digit58 = (int) number58[i] & 0xFF;
|
||||
int temp = remainder * 58 + digit58;
|
||||
|
||||
number58[i] = (byte) (temp / 256);
|
||||
|
||||
remainder = temp % 256;
|
||||
}
|
||||
|
||||
return (byte) remainder;
|
||||
}
|
||||
|
||||
private static byte[] copyOfRange(byte[] source, int from, int to) {
|
||||
byte[] range = new byte[to - from];
|
||||
System.arraycopy(source, from, range, 0, range.length);
|
||||
|
||||
return range;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user