[Webfunds-commits] java/webfunds/sox AbstractPayment.java AccountId.java DepositRequest.java Payment.java PaymentFactory.java RandomToken.java Receipt.java Token.java TokenPayment.java ValueAccount.java

Ian Grigg iang@cypherpunks.ai
Thu, 9 Nov 2000 09:13:52 -0400 (AST)


iang        00/11/09 09:13:52

  Modified:    webfunds/sox AbstractPayment.java AccountId.java
                        DepositRequest.java Payment.java
                        PaymentFactory.java RandomToken.java Receipt.java
                        Token.java TokenPayment.java ValueAccount.java
  Log:
  rewrote substantial parts to get it into WebFunds.

Revision  Changes    Path
1.2       +37 -2     java/webfunds/sox/AbstractPayment.java

Index: AbstractPayment.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/AbstractPayment.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- AbstractPayment.java	2000/08/05 13:05:39	1.1
+++ AbstractPayment.java	2000/11/09 13:13:49	1.2
@@ -1,5 +1,5 @@
 /*
- * $Id: AbstractPayment.java,v 1.1 2000/08/05 13:05:39 iang Exp $
+ * $Id: AbstractPayment.java,v 1.2 2000/11/09 13:13:49 iang Exp $
  *
  * Copyright (c) Systemics Inc 1995-2000 on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -61,7 +61,42 @@
     protected int subversion;
     public int getSubVersion()            { return subversion; }
 
+    /**
+     *  Get the account from which the transaction is drawn,
+     *  should be overridden for sourced payments.
+     */
+    public AccountId getSource() { return AccountId.BEARER; }
+     
+    /**
+     *  Get the account to which the payment is made,
+     *  should be overridden for targetted payments.
+     */
+    public AccountId getTarget() { return AccountId.BEARER; }
+    public boolean   isBearer()  { return true; }
+
+    /**
+     *  The identifer for this payment.  This is used locally for
+     *  storage and uniqueness, and may be used by the extending
+     *  payment for idempotency, or stripped off as desired.
+     */
+    protected String paymentId;
+    /**
+     *  Get the Payment Id.
+     *  should be overridden for identified payments.
+     */
+    public String getId() { return paymentId; }
+
+    /**
+     * Get the date from which the payment is valid.
+     */
+    public long getValidFrom() { return 0; }
+
+    /**
+     * Get the date to which the payment is valid
+     */
+    public long getValidTill() { return Long.MAX_VALUE; }
 
+     
 
     /**
      *  The type of item (or "currency") of this payment.
@@ -78,7 +113,7 @@
     public ItemId getItem() { return item; }
 
     /**
-     *  The quantity of the item to be transferred between accounts
+     *  The quantity of the item represented by this payment.
      */
     protected long qty;
     public long getQty() { return qty; }



1.20      +4 -2      java/webfunds/sox/AccountId.java

Index: AccountId.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/AccountId.java,v
retrieving revision 1.19
retrieving revision 1.20
diff -u -r1.19 -r1.20
--- AccountId.java	2000/08/05 13:00:56	1.19
+++ AccountId.java	2000/11/09 13:13:50	1.20
@@ -1,4 +1,4 @@
-/* $Id: AccountId.java,v 1.19 2000/08/05 13:00:56 iang Exp $
+/* $Id: AccountId.java,v 1.20 2000/11/09 13:13:50 iang Exp $
  *
  * Copyright (c) Systemics Inc. 1995-2000 on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -14,7 +14,7 @@
  * This class represents an Account identifier,
  * which is the standard way of referring to an account.
  *
- * @version $Revision: 1.19 $
+ * @version $Revision: 1.20 $
  */
 public class AccountId extends Id
 {
@@ -44,6 +44,8 @@
 
         return this.id == null || this.id.length == 0;
     }
+
+    public static final AccountId BEARER = new AccountId();   // empty is bearer
 
     public boolean equals(Object object)
     {



1.26      +34 -35    java/webfunds/sox/DepositRequest.java

Index: DepositRequest.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/DepositRequest.java,v
retrieving revision 1.25
retrieving revision 1.26
diff -u -r1.25 -r1.26
--- DepositRequest.java	2000/10/14 01:36:59	1.25
+++ DepositRequest.java	2000/11/09 13:13:50	1.26
@@ -1,5 +1,5 @@
 /*
- * $Id: DepositRequest.java,v 1.25 2000/10/14 01:36:59 iang Exp $
+ * $Id: DepositRequest.java,v 1.26 2000/11/09 13:13:50 iang Exp $
  *
  * Copyright (c) Systemics Ltd 1995-1999 on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -44,8 +44,10 @@
     /**
      * The payment to be deposited
      * (as originally created by the payer)
+     * (was Payment).
      */
-    protected Payment payment;
+    protected AbstractPayment payment;
+    protected byte[]          payBuf;
 
     /**
      * An identifier for this deposit request
@@ -70,7 +72,8 @@
     /**
      * Get the payment to be deposited
      */
-    public Payment getPayment() { return payment; }
+    public Payment         getPayment()  { return (Payment)payment; } // old
+    public AbstractPayment getPay()      { return payment; }          // v >= 3
 
     /**
      * Get the identifier on the payment, as originally created by the payer
@@ -103,13 +106,13 @@
 
     /**
      * Get the date from which the payment is valid
-     */
     public long getPaymentValidFrom() { return payment.getValidFrom(); }
+     */
 
     /**
      * Get the date to which the payment is valid
-     */
     public long getPaymentValidTill() { return payment.getValidTill(); }
+     */
 
     /**
      * Get the description on the payment
@@ -179,12 +182,22 @@
      * @param depositId the deposit request identifier (must be unique)
      * @param desc depositor's description of what this transaction is for
      */
-    public DepositRequest(String requestId, AccountId acc, Payment payment, String depositId, byte[] desc)
+    public DepositRequest(String requestId, AccountId acc,
+                          AbstractPayment payment,
+                          String depositId, byte[] desc)
     {
         super(requestId, acc);
         this.payment = payment;
         this.depositId = depositId;
         depositDesc = desc;
+
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+            payment.encode(baos);
+        } catch (IOException ex) {
+            throw new IllegalArgumentException("DR pay: " + ex);
+        }
+        this.payBuf = baos.toByteArray();
     }
 
     /**
@@ -236,28 +249,21 @@
         DataOutputStream dos = new DataOutputStream(os);
         super.encode(dos);
 
-// will I need this???
-//        encode_payload(dos);
-//    }
-//
-//    protected void encode_payload(DecodeOutputStream dos)
-//        throws IOException
-//    {
-//        /* DataOutputStream dos = new DataOutputStream(os); ??? */
-
-        // New! for version == 2 && super.version == 3
+        // New for version == 2 && super.version == 3
         if (req_version >= REQ_SUBS_ADD_V)
             dos.writeByte(version); 
  
-        payment.encode(dos);
-        writeString(dos, depositId);
-        writeByteArray(dos, depositDesc);
-
         if (version >= DEPOSIT_WITHDRAW)  // New!  I can jump technologies...
         {
             dos.writeByte(typeOfPayment);
             dos.writeByte(protocolState);
+            writeByteArray(dos, payBuf);
         }
+        else
+            payment.encode(dos);
+
+        writeString(dos, depositId);
+        writeByteArray(dos, depositDesc);
     }
 
 
@@ -276,15 +282,6 @@
         DataInputStream dis = new DataInputStream(is);
         super.decode(dis);
     
-// will I need this???
-//        decode_payload(dis);
-//    }
-//
-//    protected void decode_payload(DataInputStream is)
-//        throws IOException, SOXPacketException
-//    {
-//        /* DataInputStream dis = new DataInputStream(is); ??? */
-    
         /*
          *  the problem here being that all receipts include the
          *  deposit, which will need to be recovered, as long as
@@ -294,22 +291,24 @@
         if (req_version >= REQ_SUBS_ADD_V)    // parent, adds my version num
         {
             v = dis.readUnsignedByte();
-            if (! ((DEPOSIT_LOCAL_VERS <= v) && (v <=DEPOSIT_WITHDRAW)) )
+            if (! ((DEPOSIT_LOCAL_VERS <= v) && (v <= DEPOSIT_WITHDRAW)) )
                 throw new SOXPacketException("Invalid version in DepositR:" +
                     v + " != {"+DEPOSIT_LOCAL_VERS+","+DEPOSIT_WITHDRAW+"}");
             version = v;         // remember this for sig checks
         }
 
-        payment = new Payment(dis);
-        depositId = readString(dis);
-
-        depositDesc = readByteArray(dis);
-
         if (version >= DEPOSIT_WITHDRAW)  // New!  I can jump technologies...
         {
             typeOfPayment = dis.readUnsignedByte();
             protocolState = dis.readUnsignedByte();
+            payBuf = readByteArray(dis);
+            payment = PaymentFactory.decode(payBuf);
         }
+        else
+            payment = new Payment(dis);
+
+        depositId = readString(dis);
+        depositDesc = readByteArray(dis);
     }
 
     public String toAscii()



1.24      +4 -4      java/webfunds/sox/Payment.java

Index: Payment.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/Payment.java,v
retrieving revision 1.23
retrieving revision 1.24
diff -u -r1.23 -r1.24
--- Payment.java	2000/10/14 01:28:11	1.23
+++ Payment.java	2000/11/09 13:13:50	1.24
@@ -1,5 +1,5 @@
 /*
- * $Id: Payment.java,v 1.23 2000/10/14 01:28:11 iang Exp $
+ * $Id: Payment.java,v 1.24 2000/11/09 13:13:50 iang Exp $
  *
  * Copyright (c) Systemics Inc 1995-2000 on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -55,9 +55,9 @@
 
 
     /**
-     * The identifer for this payment
-     */
+     * The identifer for this payment - now in AbstractPayment.
     protected String paymentId;
+     */
 
     /**
      * The account from which the payment is drawn
@@ -100,8 +100,8 @@
      *  excluding six magic characters:
      *
      *             -|:{}#
-     */
     public String getId() { return paymentId; }
+     */
 
     /**
      * Get the account from which the transaction is drawn



1.5       +115 -1    java/webfunds/sox/PaymentFactory.java

Index: PaymentFactory.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/PaymentFactory.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- PaymentFactory.java	2000/10/14 01:28:48	1.4
+++ PaymentFactory.java	2000/11/09 13:13:50	1.5
@@ -1,4 +1,4 @@
-/* $Id: PaymentFactory.java,v 1.4 2000/10/14 01:28:48 iang Exp $
+/* $Id: PaymentFactory.java,v 1.5 2000/11/09 13:13:50 iang Exp $
  *
  * Copyright (c) Systemics Inc. 1995-2000 on behalf of
  * The WebFunds Development Team. All Rights Reserved.
@@ -98,4 +98,118 @@
         }
         return typeNames[type] + " (" + type + ")";
     }
+
+
+    /**
+     *  @return the quantity from a coin size, which is always logarithmic,
+     *          if outside range, -1 is returned.
+     */
+    public static long log2qty(int log)
+    {
+        if (! (0 <= log && log <= 63) )
+            return -1;
+        if (log == 0)
+            return 0;
+
+        long q = 1;
+        while (--log > 0)
+            q <<= 1;
+        return q;
+    }
+
+    /**
+     *  @return a single largest coin size from a quantity.
+     */
+    public static int qty2log(long qty)
+    {
+        if (qty < 0)
+            throw new IllegalArgumentException("qty < 0: " + qty);
+
+        int i = 0;
+        while (qty != 0)
+        {
+            qty >>= 1;
+            i++;
+        }
+        return i;
+    }
+
+    /**
+     *  @return an array representing the coin sizes
+     *          required for this quantity
+     */
+    public static int[] qty2coins(long qty)
+    {
+        if (qty < 0)
+            throw new IllegalArgumentException("qty < 0: " + qty);
+
+        int num = 0;
+        long q;
+        for (q = qty; q != 0; q >>= 1)
+        {
+            if ( (q & 01) == 01 )
+                num++;
+        }
+        int[] coins = new int[num];
+
+        int i = 0;
+        int size = 0;
+        for (q = qty; q != 0; q >>= 1)
+        {
+            size++;
+            if ( (q & 01) == 01 )
+                coins[i++] = size;
+        }
+
+        return coins;
+    }
+
+    public static void main(String[] args)
+    {
+        if (log2qty(0) != 0)
+            throw new RuntimeException("log(0) != 0 : " + log2qty(0));
+        if (log2qty(1) != 1)
+            throw new RuntimeException("log(1) != 1 : " + log2qty(1));
+        if (log2qty(4) != 8)
+            throw new RuntimeException("log(4) != 8 : " + log2qty(4));
+        if (log2qty(63) != 4611686018427387904L)
+            throw new RuntimeException("log(63) != 4611686018427387904 : " +
+                                       log2qty(63));
+        if (qty2log(0) != 0)
+            throw new RuntimeException("qty(0) != 0 : " + qty2log(0));
+        if (qty2log(1) != 1)
+            throw new RuntimeException("qty(1) != 1 : " + qty2log(1));
+        if (qty2log(31) != 5)
+            throw new RuntimeException("qty(31) != 5 : " + qty2log(31));
+        if (qty2log(2147483647) != 31)
+            throw new RuntimeException("qty(2147483647) != 31 : " +
+                                       qty2log(2147483647));
+
+        if (qty2coins(0).length != 0)
+            throw new RuntimeException("coins(0) != 0 : " + qty2coins(0).length);
+        if (qty2coins(1).length != 1)
+            throw new RuntimeException("coins(1) != 1 : " + qty2coins(1).length);
+        if (qty2coins(16).length != 1)
+            throw new RuntimeException("coins(16) != 1 : " + qty2coins(16).length);
+        if (qty2coins(17).length != 2)
+            throw new RuntimeException("coins(17) != 2 : " + qty2coins(17).length);
+        if (qty2coins(19).length != 3)
+            throw new RuntimeException("coins(19) != 3 : " + qty2coins(19).length);
+
+        for (int i = 0; i <= 16; i++)
+        {
+            int log = qty2log(i);
+            long qty = log2qty(i);
+            System.out.print(i + ": " + log + "\t" + qty + "\t" +
+                                        qty2log(qty) + "\t" + log2qty(log));
+
+            int[] coins = qty2coins(i);
+            for (int j = coins.length - 1; j >= 0; j--)
+                 System.out.print("\t" + coins[j]);
+
+            System.out.println("");
+        }
+    }
+
+
 }



1.2       +190 -25   java/webfunds/sox/RandomToken.java

Index: RandomToken.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/RandomToken.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- RandomToken.java	2000/08/05 13:05:39	1.1
+++ RandomToken.java	2000/11/09 13:13:50	1.2
@@ -1,5 +1,5 @@
 /*
- * $Id: RandomToken.java,v 1.1 2000/08/05 13:05:39 iang Exp $
+ * $Id: RandomToken.java,v 1.2 2000/11/09 13:13:50 iang Exp $
  *
  * Copyright (c) Systemics Inc 1995-2000 on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -10,7 +10,16 @@
 import java.io.OutputStream;
 import java.io.ByteArrayOutputStream;
 import java.io.IOException;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
 
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.KeyException;
+import java.security.SecureRandom;
+
+import webfunds.utils.Hex;
+
 /**
  *  This class represents a Big Random Number Token.
  *
@@ -32,27 +41,50 @@
 
     /**
      * The type of token encoded by this class.
+     */
+    public static final int RANDOM_TYPE = PaymentFactory.RANDOM_TOKEN;
+
+
+
+    /**
+     *  The signature on the token (generally made by the mint).
+     *  How it is made and verified is coded within the crypto-system
+     *  as found in sign().
+     */
+    protected byte[] sig;
+    /**
+     *  @return the signature on the token (never null)
      */
-    public static final int RANDOM_TYPE = 1;
+    public byte[] getSignature()                { return sig; }
 
 
+    /**
+     *  The token payload (generally made by the client).
+     *  How it is made and verified is coded within the crypto-scheme
+     *  as found in prototype().
+     */
+    protected byte[] token;
+    /**
+     *  @return the token payload (never null)
+     */
+    public byte[] getPayload()                  { return token; }
+
 
+
+
     /**
      * Create a RandomToken
      *
-     * @param byte[] random big random number (cannot be null)
-     * @param qty is the amount (of unit of account) this token represents
-     * @param expiry the Java time when this (batch) expires
+     * @param log the coin size, log base 2 of quantity
      */
-    public RandomToken(byte[] random, long qty, long expiry)
+    public RandomToken(int log)
     {
-        super(RANDOM_TYPE, SUB_VERSION, qty, expiry);
+        super(RANDOM_TYPE, SUB_VERSION, log);
 
-        setPayload(random);
-        if (random.length > 0)
-            setState(TOK_VALID);
+        this.sig = this.token = new byte[0];
     }
 
+
     public RandomToken(byte[] paymentData)
         throws SOXPacketException
     {
@@ -67,6 +99,92 @@
 
 
     /**
+     *  Make a set of Tokens that can handle a quantity.
+     *  Simply calls qty2coins() then uses those sizes.
+     */
+    public static Token[] getProtoTokens(long qty)
+    {
+        return getProtoTokens( PaymentFactory.qty2coins(qty) );
+    }
+
+    /**
+     *  Make a set of Tokens matched to a set of coin sizes.
+     */
+    public static Token[] getProtoTokens(int[] coins)
+    {
+        int len = coins.length;
+        Token[] tokens = new Token[len];
+
+        for (int i = 0; i < len; i++)
+        {
+             int size = coins[i];             // log size of this coin
+             RandomToken token = new RandomToken(size);
+             token.prototype();               // set up the random stuff
+             tokens[i] = token;
+        }
+        return tokens;
+    }
+
+
+
+/////////  Phases  //////////////////////////////////////////////
+
+    public void sign(PrivateKey key, long expiry)
+        throws KeyException
+    {
+        this.expiry = expiry;
+        sig = Crypto.sign(key, token);
+        state = TOK_SIGNED;
+    }
+
+    private void cheatSign(long expiry)        // for example(), no key needed
+    {
+        this.expiry = expiry;
+        sig = Utils.exampleData(20);
+        state = TOK_SIGNED;
+    }
+
+    public boolean verify(PublicKey key)
+        throws KeyException
+    {
+        return Crypto.verify(key, token, sig);
+    }
+
+
+
+    /**
+     *  Create the prototype coin.
+     *  Normally called by client, no standard paramaters.
+     */
+    public void prototype()
+    {
+        SecureRandom sr = Crypto.getSecureRandom();
+        token = new byte[20];             // SHA1 sized, whatever
+        sr.nextBytes(token);
+        state = TOK_PROTO;
+    }
+
+    private void cheatProto()               // for example(), quicker
+    {
+        token = Utils.exampleData(20);
+        state = TOK_PROTO;
+    }
+
+    /**
+     *  Unblind the signed coin, revealing the signed, private coin.
+     *  Normally called by client, no standard paramaters.
+     */
+    public void unblind()
+    {
+        state = TOK_UNBLIND;
+    }
+
+
+
+
+/////////  Wire  //////////////////////////////////////////////
+
+    /**
      * Update this payment object with the values from
      * a payment encoded as a byte array (such as previously
      * returned from the encode() method of a payment object).
@@ -79,16 +197,22 @@
     public void decode(InputStream is)
         throws IOException, SOXPacketException
     {
-        super.decode(is);
+        DataInputStream dis = new DataInputStream(is);
 
+        super.decode(dis);
+
         if (subversion != SUB_VERSION)
             throw new SOXPacketException("Invalid subversion in token: "+
                       subversion + " != " + SUB_VERSION);
 
         if (type != RANDOM_TYPE)
             throw new SOXPacketException("not RandomToken (" +subversion+ ")");
+
+        // we have to save expiry if we want it
+        expiry = dis.readLong();
 
-        /* nothing to add */
+        sig = readByteArray(dis);          // always returns array
+        token = readByteArray(dis);
     }
 
     /**
@@ -102,9 +226,14 @@
     public void encode(OutputStream os)
         throws IOException
     {
-        super.encode(os);
+        DataOutputStream dos = new DataOutputStream(os);
 
-        /* nothing to add */
+        super.encode(dos);
+
+        dos.writeLong(expiry);
+
+        writeByteArray(dos, sig);
+        writeByteArray(dos, token);
     }
 
 
@@ -113,32 +242,68 @@
 
     public String toString()
     {
-        return "RandomToken " + vString() + " T" + type + ": " + super.toString();
+        String s = "RandomToken " + vString() + " T" + type + ":\n";
+        s += super.toString();
+        if (isRaw())
+            return s;
+        s += "\n  tok: " + Hex.data2hex(token);
+        if (isProto())
+            return s;
+        s += "\n  sig: " + Hex.data2hex(sig) +"\n";
+        return s;
     }
 
     public static RandomToken example()
     {
         byte b = Utils.exampleByte();
-        long expiry = Utils.exampleLong();           // might be expired
+        if (b < 0)
+            b = (byte)-b;
+        byte log = (byte) (b % 64);
 
-        long qty  = Utils.exampleLong();
-        if (qty < 0)
-            qty = -qty;
-
         byte[] random;
         random = Utils.exampleData();
 
         RandomToken obj;
-        obj = new RandomToken(random, expiry, qty);
+        obj = new RandomToken(log);       // in TOK_RAW state
 
+        b = Utils.exampleByte();
         /* initialised in TOK_VALID */
-        if ((b & 0x30) == 0x30)
-            obj.setState(TOK_SPENT);
-        else if ((b & 0xB0) == 0xB0)
-            obj.setState(TOK_PROTO);
+        if ((b & 0x3) >= 1)
+        {
+            obj.cheatProto();
+        }
+
+        if ((b & 0x3) >= 2)
+        {
+            long expiry = Utils.exampleLong();      // might be expired
+            if (expiry < 0)
+                expiry = -expiry;
+            obj.cheatSign(expiry);
+        }
+
+        if ((b & 0x3) >= 2)
+            obj.setState(b & 0x7);
 
         return obj;
     }
+
+    public boolean equals(java.lang.Object obj)
+    {
+        if (obj == null || !super.equals(obj))
+            return false;
+        if (!(obj instanceof RandomToken))
+            return false;
+     
+        RandomToken other = (RandomToken)obj;
+       
+        if (!Utils.byteEquals(sig, other.getSignature()))
+            return false ;
+        if (!Utils.byteEquals(token, other.getPayload()))
+            return false ;
+     
+        return true;
+    }
+
 
     public static void main(String[] args)
     {



1.41      +33 -20    java/webfunds/sox/Receipt.java

Index: Receipt.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/Receipt.java,v
retrieving revision 1.40
retrieving revision 1.41
diff -u -r1.40 -r1.41
--- Receipt.java	2000/10/14 01:31:06	1.40
+++ Receipt.java	2000/11/09 13:13:50	1.41
@@ -1,5 +1,5 @@
 /*
- * $Id: Receipt.java,v 1.40 2000/10/14 01:31:06 iang Exp $
+ * $Id: Receipt.java,v 1.41 2000/11/09 13:13:50 iang Exp $
  *
  * Copyright (c) Systemics Ltd 1995-1999 on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -44,8 +44,8 @@
     protected DepositRequest depositRequest;
     protected byte[]         depositRequestData;
 
-    protected AbstractPayment protoPayment;
-    protected byte[]          protoPaymentData;
+    protected AbstractPayment exPayment;
+    protected byte[]          exPaymentData;
 
     protected ItemId    item;
     protected long qty;
@@ -76,12 +76,20 @@
     public Payment getPayment() { return depositRequest.getPayment(); }
 
     /**
-     *  Get the Proto-Payment if the original request was a WithdrawalRequest.
+     *  Get the Payment that was deposited.
      */
-    public AbstractPayment getProtoPayment()       { return protoPayment; }
-    public void setProtoPayment(AbstractPayment p) { protoPayment = p; }
+    public AbstractPayment getPay() { return depositRequest.getPay(); }
 
     /**
+     *  Get the Exchange Payment if the original request
+     *  was a WithdrawalRequest.  This will be a proto
+     *  payment to start off with, and the mint will
+     *  replace it with a signed payment.
+     */
+    public AbstractPayment getExchangePayment()    { return exPayment; }
+    public void setExchangePayment(AbstractPayment p) { exPayment = p; }
+
+    /**
      * Get the DepositRequest object that was used to deposit the payment
      *
      * The DepositRequest is the signed object
@@ -276,8 +284,10 @@
         //  Unix backends truncate the time.
         //  The result is a timestamp that is seconds multiplied by a 1000.
         //
-        long from = req.getPaymentValidFrom() - 999;
-        long till = req.getPaymentValidTill();
+        AbstractPayment p = req.getPay();
+        long from = p.getValidFrom() / 1000;
+        from *= 1000;
+        long till = p.getValidTill();
 
         long time = getTimestamp();
         if ( ! (from < time && time < till) &&
@@ -287,8 +297,8 @@
                                          " not in "+from+","+till);
         }
 
-        protoPayment = null;
-        protoPaymentData = null;
+        exPayment = null;
+        exPaymentData = null;
         flags = 0;
     }
 
@@ -307,10 +317,10 @@
     public Receipt(byte[] receiptData)
         throws SOXPacketException
     {
-        PrintWriter temp = debug;
-        debug = null;
+//        PrintWriter temp = debug;
+//        debug = null;
         decode(receiptData);
-        debug = temp;
+//        debug = temp;
     }
 
     /**
@@ -424,8 +434,8 @@
             flags = dis.readLong();
 
         //
-        // Perl code shoves a one byte Message Digest Type in front of hashes.
-        // See OpenPGP for the types.
+        //  Perl code shoves a one byte (OpenPGP) Message Digest Type
+        //  in front of hashes.
         //
         int hash_type = dis.readUnsignedByte();
         if (MD_SHA1 != hash_type)
@@ -455,8 +465,8 @@
 
         if (version >= RECEIPT_PROTO && isWithdrawalReceipt())
         {
-            protoPaymentData = readByteArray(dis);
-            protoPayment = new Payment(protoPaymentData);
+            exPaymentData = readByteArray(dis);
+            exPayment = new Payment(exPaymentData);
         }
 
         sig = readByteArray(dis);
@@ -552,7 +562,7 @@
         // written as array, as original might vary on encode()
         writeByteArray(dos, depositRequestData);
         if (version >= RECEIPT_PROTO && isWithdrawalReceipt())
-            writeByteArray(dos, protoPaymentData);
+            writeByteArray(dos, exPaymentData);
     }
 
     /**
@@ -622,9 +632,12 @@
         DepositRequest req = DepositRequest.example();
 
 //System.err.println("Payment: " + req.getPayment());
+
+        AbstractPayment p = req.getPay();
+        long from = p.getValidFrom() / 1000;
+        from *= 1000;
+        long till = p.getValidTill();
 
-        long from = req.getPaymentValidFrom() - 999;
-        long till = req.getPaymentValidTill();
         long relTime = Utils.exampleLong();
         if (relTime < 0) relTime = -relTime;
         relTime = relTime % (till - from);



1.2       +92 -48    java/webfunds/sox/Token.java

Index: Token.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/Token.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- Token.java	2000/08/05 13:05:39	1.1
+++ Token.java	2000/11/09 13:13:50	1.2
@@ -1,5 +1,5 @@
 /*
- * $Id: Token.java,v 1.1 2000/08/05 13:05:39 iang Exp $
+ * $Id: Token.java,v 1.2 2000/11/09 13:13:50 iang Exp $
  *
  * Copyright (c) Systemics Inc 1995-2000 on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -13,7 +13,9 @@
 import java.io.OutputStream;
 import java.io.DataOutputStream;
 
-// import java.security.PublicKey;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.KeyException;
 
 import webfunds.utils.Hex;
 
@@ -67,16 +69,42 @@
     public int getState()                       { return state; }
     public void setState(int state)             { this.state = state; }
 
-    public static final int            TOK_PROTO  = 0,
-                                       TOK_VALID  = 1,
-                                       TOK_SIGNED = 2,
-                                       TOK_BLIND  = 3,
-                                       TOK_SPENT  = 4;
+    public static final int            TOK_RAW      = 0,  // empty, valueless
+                                       TOK_PROTO    = 1,  // clean, valueless
+                                       TOK_SIGNED   = 2,  // by mint, has value
+                                       TOK_UNBLIND  = 3,  // untraceable form
+
+                                       TOK_RECEIVED = 4,    // spent to me
+                                       TOK_SPENT    = 5,    // spent to other
+                                       TOK_SETTLED  = 6,    // complete
+
+                                       TOK_VALID    = 10; // mint wants to sign
+
+    public boolean isRaw()             { return state == TOK_RAW; }
+    public boolean isProto()           { return state == TOK_PROTO; }
+    public boolean isSigned()          { return state == TOK_SIGNED; }
+    public boolean isUnblinded()       { return state == TOK_UNBLIND; }
+    public boolean isReceived()        { return state == TOK_RECEIVED; }
+    public boolean isSpent()           { return state == TOK_SPENT; }
+    public boolean isSettled()         { return state == TOK_SETTLED; }
 
     /**
      *  The quantity of the item that this token represents.
+     *  Encoded in logarithm - base 2 of the quantity.
+     *
+     *         Log    Qty
+     *          0      0
+     *          1      1
+     *          2      2
+     *          3      4
+     *          4      8
+     *
+     *  and so forth.  -1 could be used for "not stated", meaning it is
+     *  encoded in the magic parts (by use of certain key, for example).
      */
-    protected long qty;
+    protected int log;
+    public long getLog()             { return log; }
+
     /**
      *  Note (1) that this may be derived information, the real
      *  token value may be encoded in the signature key or
@@ -86,9 +114,11 @@
      *  the token, the higher layer code should remember that.
      *  See Payments for the encoded Item (a.k.a. Ricardian Contract).
      *
-     *  @return quantity of the item that this token represents.
+     *  @return quantity of the item that this token represents,
+     *          being 0 - n representing which bit,
+     *          or -1 if "not so encoded"
      */
-    public long getQty()                        { return qty; }
+    public long getQty()             { return PaymentFactory.log2qty(log); }
 
     /**
      *  Tokens commonly come from a batch with a defined expiry date.
@@ -102,21 +132,21 @@
      *  (generally made by the mint according to the protocol).
      *  How it is made and verified is up to the higher layers.
      *  May be ignored (for technical reasons, cannot be null).
-     */
     protected byte[] token;
+     */
     /**
      *  @return the token payload (never null)
-     */
     public byte[] getPayload()                  { return token; }
+     */
     /**
      *  @param a new payload for this token (null becomes empty array)
-     */
     public void   setPayload(byte[] b)
     {
         if (b == null)
             b = new byte[0];
         this.token = b;
     }
+     */
 
     /**
      *  The signature on the token
@@ -131,40 +161,66 @@
     public byte[] getSignature()                { return sig; }
     /**
      *  @param a new signature on this token (null becomes empty array)
-     */
     public void   setSignature(byte[] sig)
     {
         if (sig == null)
             sig = new byte[0];
         this.sig = sig;
     }
+     */
+
     /**
      * Check to see if this token is signed.
      * NOTE: This does not check the validity of the signature,
      * only that this token has one.
      * @return boolean true if this token has a signature
-     */
     public boolean isSigned()                   { return (sig.length > 0); }
+     */
 
 
+    /**
+     *  Sign a protocoin.
+     *  Normally called by server (mint), so it is standardised.
+     *  How it is done is up to the individual type of token, but
+     *  we hope we can limit it to this simple interface, as there
+     *  are half a dozen different token crypto-schemes out there.
+     *
+     *  @param expiry the Java time when this (batch) expires
+     *  @param key is the signing key of the mint
+     */
+    public abstract void sign(PrivateKey key, long expiry)
+        throws KeyException;
+    /**
+     *  Sign a protocoin.
+     *  Normally called by client to check the coin is signed,
+     *  matched pair with sign() so it can be a standardised call.
+     *
+     *  @param key is the signer's public key (of the mint)
+     */
+    public abstract boolean verify(PublicKey key)
+        throws KeyException;
+
 
+
+
     /**
-     *  Create an unsigned token.
+     *  Create an uninitialised token.
+     *  Call prototype() with some params to make it a real proto-token.
      *
-     *  @param desc a description of what this token is for (optional)
-     *  @param qty the number of items of which the token is for
-     *  @param expiry the Java time till which the token is valid
+     *  @param type of token, being the blinding or coin scheme
+     *  @param subversion is the version of the parent coin class
+     *  @param log the coin size, log base 2 of quantity
      */
-    public Token(int type, int subversion, long qty, long expiry)
+    public Token(int type, int subversion, int log)
     {
         this.type       = type;
         this.subversion = subversion;
-        this.qty        = qty;
-        this.expiry     = expiry;
+        this.log        = log;
 
-        this.state = TOK_PROTO;
+        this.expiry     = 0;
+        this.state = TOK_RAW;
 
-        this.sig = this.token = new byte[0];
+        // this.sig = this.token = new byte[0];
     }
 
     /**
@@ -175,10 +231,10 @@
      *  @param token the previously encoded token
      *  @excep SOXPacketException The token data is badly formatted
      */
-    public Token(byte[] token)
+    public Token(byte[] buf)
         throws SOXPacketException
     {
-        decode(token);
+        decode(buf);
     }
 
     /**
@@ -234,11 +290,7 @@
 
 
         state = dis.readUnsignedByte();
-        qty = dis.readLong();
-        expiry = dis.readLong();
-
-        sig = readByteArray(dis);          // always returns array
-        token = readByteArray(dis);
+        log = dis.readUnsignedByte();
 
     }
 
@@ -259,11 +311,7 @@
         dos.writeByte(subversion);
 
         dos.writeByte(state);
-        dos.writeLong(qty);
-        dos.writeLong(expiry);
-
-        writeByteArray(dos, sig);
-        writeByteArray(dos, token);
+        dos.writeByte(log);
     }
 
 
@@ -272,12 +320,13 @@
 
     public String toString()
     {
-        String s = "";     /* does not say what object, leave to subclass */
-
-        s += "  Q: " + qty;
-        s += "  exp: " + expiry + "\n";
-        s += "  sig: " + Hex.data2hex(sig) +"\n";
-        s += "  tok: " + Hex.data2hex(token) +"\n";
+        String s = "    state == " + state;
+        if (isRaw())
+            return s;
+        s += "    L: " + log + "\tQ: " + PaymentFactory.log2qty(log);
+        if (isProto())
+            return s;
+        s += "    exp: " + expiry + "\n";
 
         return s;
     }
@@ -296,15 +345,10 @@
 
         if (other.state != state)
             return false;
-        if (other.qty != qty)
+        if (other.log != log)
             return false;
         if (other.expiry != expiry)
             return false;
-
-        if (!Utils.byteEquals(sig, other.getSignature()))
-            return false ;
-        if (!Utils.byteEquals(token, other.getPayload()))
-            return false ;
 
         return true;
     }



1.2       +25 -13    java/webfunds/sox/TokenPayment.java

Index: TokenPayment.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/TokenPayment.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- TokenPayment.java	2000/08/05 13:05:39	1.1
+++ TokenPayment.java	2000/11/09 13:13:50	1.2
@@ -1,5 +1,5 @@
 /*
- * $Id: TokenPayment.java,v 1.1 2000/08/05 13:05:39 iang Exp $
+ * $Id: TokenPayment.java,v 1.2 2000/11/09 13:13:50 iang Exp $
  *
  * Copyright (c) Systemics Inc 1995-2000 on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -51,20 +51,20 @@
     /**
      *  The account from which the payment was drawn.
      *  This is meaningless in a token concept, just here for
-     *  informational purposes.
+     *  informational purposes.  A client would probably strip
+     *  this on sending it out.
      */
     protected AccountId source;
     /** Get the account from which the transaction is drawn */
-    public AccountId getSource() { return source; }
+    public AccountId getSource()             { return source; }
+    public void      setSource(AccountId id) { source = id; }
 
-    /**
-     *  The account to which the payment is made out.
-     *  This is meaningless in a token concept, just here for
-     *  informational purposes.
-     */
-    public AccountId getTarget() { return new AccountId(); }
-    /** @return true if the payment is made out to BEARER (always true) */
+    /*
+     *  The account to which the payment is made out is always BEARER,
+     *  so AbstractPayment covers that fine.
+    public AccountId getTarget() { return AccountId.BEARER; }
     public boolean isBearer() { return true; }
+     */
 
     /**
      *  List of Tokens.
@@ -73,6 +73,10 @@
     /** Get the tokens within the payment */
     public Token[] getTokens() { return tokens; }
 
+    /**
+     *  Set the paymentId, so that you can strip it clean on export.
+     */
+    public void      setId(String id) { paymentId = id; }
 
 
     /**
@@ -85,17 +89,22 @@
      *  The items is indicated so that a pointer to a valid SOXServer
      *  can be determined at the receiving end.
      *
+     *  Validity times are ignored here, although real token systems
+     *  do have times involved.
+     *
      *  @param source the source account, can be empty / null
      *  @param item the item for which the payment is for
      *  @param desc a description of what this payment is for (null ==> empty)
      *  @param tokens an array of tokens, cannot be null or contain nulls,
      *         may be empty
+     *  @param pid identifier for local storage only
      */
     public TokenPayment(
+                String pid,
                 AccountId source,
                 ItemId item,
-                byte[] desc,
-                Token[] tokens
+                Token[] tokens,
+                byte[] desc
                 )
     {
         super(PF_VERSION, TOKEN_TYPE, TOKEN_VERSION, item, 0, desc);
@@ -107,6 +116,8 @@
             source = new AccountId();
         this.source = source;
 
+        this.paymentId = pid;
+
         this.tokens = tokens;
 
         if (tokens == null)
@@ -294,6 +305,7 @@
         byte[] desc = Utils.exampleData();
         if ((b & 0xF0) == 0xF0)
             desc = null;
+        String pid = Utils.exampleString();
 
         AccountId ac = AccountId.example();
         if ((b & 0xD0) == 0xD0)
@@ -307,7 +319,7 @@
             tokens[i] = RandomToken.example();
 
         TokenPayment obj;
-        obj = new TokenPayment(ac, item, desc, tokens);
+        obj = new TokenPayment(pid, ac, item, tokens, desc);
 
         return obj;
     }



1.21      +45 -10    java/webfunds/sox/ValueAccount.java

Index: ValueAccount.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/ValueAccount.java,v
retrieving revision 1.20
retrieving revision 1.21
diff -u -r1.20 -r1.21
--- ValueAccount.java	2000/10/14 01:50:45	1.20
+++ ValueAccount.java	2000/11/09 13:13:50	1.21
@@ -1,5 +1,5 @@
 /*
- * $Id: ValueAccount.java,v 1.20 2000/10/14 01:50:45 iang Exp $
+ * $Id: ValueAccount.java,v 1.21 2000/11/09 13:13:50 iang Exp $
  *
  * Copyright (c) Systemics Ltd 1995-1999 on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -409,21 +409,27 @@
     }
 
     /**
-     * Create a rollover.  This causes all funds to transfer to the
-     * target and the source to *FROZEN* ....  Not working yet!
+     *  No real need for this, let caller do it directly?
      *
-     * @param target the target account where all funds end up for all subs
-     * @param qty the number of items of which the payment is for
-     * @param desc a description of what this payment is for (optional)
-     * @param validity the payment is valid for validity seconds (from now)
-     */
-    public Payment createRollover(AccountId target, byte[] desc, long validity)
+     *  Create a rollover payment.  This causes all funds to transfer to the
+     *  target and the source to be *FROZEN* at the issuer ....
+     *  Not working yet!
+     *
+     *  We don't use the word freeze because accounts also get frozen
+     *  here.
+     *
+     *  @param target the target account where all funds end up for all subs
+     *  @param qty the number of items of which the payment is for
+     *  @param desc a description of what this payment is for (optional)
+     *  @param validity the payment is valid for validity seconds (from now)
+    public Payment createRollover(AccountId target, byte[] desc, long validTill)
         throws SOXSubAccountException
     {
         long now = System.currentTimeMillis();
         long validTill = now + validity*1000;
         return createPayment(target, 0, desc, true, now, validTill, null);
     }
+     */
 
     /**
      * Create a payment, with all params specified.
@@ -435,7 +441,9 @@
      * @param from the time from which the payment is valid
      * @param till the time at which the payment will expire
      * @param pid a unique payment identifier, replaced if empty
-     */
+
+What is the point of a Date Conversion method?  Make the caller do that.
+
     public Payment createPayment(AccountId target, long qty, byte[] desc,
                                  boolean changekey, Date from, Date till,
                                  String pid)
@@ -467,6 +475,7 @@
         logmsg("PAYMENT IS SIGNED: " + signed);
         return payment;
     }
+     */
 
     /**
      * Create a payment, with all params specified.
@@ -508,6 +517,32 @@
 
         boolean signed = payment.verify(acct.getPublicKey());
         logmsg("PAYMENT IS SIGNED: " + signed);
+        return payment;
+    }
+
+    /**
+     *  Create a proto token payment.
+     *  A little different to normal payments, as there are
+     *  no special needs like signing, so this call is not needed.
+     *
+     * @param tokens the individual tokens or coins that need withdrawing
+     * @param desc a description of what this payment is for (optional)
+     * @param pid a unique payment identifier, replaced if empty
+     */
+    public TokenPayment createTokenPayment(Token[] tokens,
+                                 byte[] desc,
+                                 String pid)
+        throws SOXSubAccountException
+    {
+        checkFrozen("createTokenPayment");
+
+        if (pid == null || pid.length() == 0)
+            pid = "P" + System.currentTimeMillis();
+
+        TokenPayment payment = new TokenPayment(pid, acct.getId(),
+                                      itemId, tokens,
+                                      desc);
+
         return payment;
     }