[Webfunds-commits] java/webfunds/sox Receipt.java

Ian Grigg iang@cypherpunks.ai
Fri, 6 Apr 2001 19:52:41 -0400 (AST)


iang        01/04/06 19:52:41

  Modified:    webfunds/sox Receipt.java
  Log:
  additional support for AbstractPayments and Float accounts

Revision  Changes    Path
1.44      +214 -59   java/webfunds/sox/Receipt.java

Index: Receipt.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/Receipt.java,v
retrieving revision 1.43
retrieving revision 1.44
diff -u -r1.43 -r1.44
--- Receipt.java	2001/03/23 15:06:03	1.43
+++ Receipt.java	2001/04/06 23:52:40	1.44
@@ -1,15 +1,27 @@
 /*
- * $Id: Receipt.java,v 1.43 2001/03/23 15:06:03 iang Exp $
+ * $Id: Receipt.java,v 1.44 2001/04/06 23:52:40 iang Exp $
  *
  * Copyright (c) Systemics Ltd 1995-1999 on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
  */
 package webfunds.sox;
 
-import java.io.*;
-import java.security.*;
+import java.security.PrivateKey;
+import java.security.PublicKey;
+import java.security.KeyException;
+
+import java.io.IOException;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.PrintWriter;
 
+import webfunds.utils.Panic;
 
+
 /**
  * This class represents a signed receipt,
  * such as received from an issuer in response
@@ -39,14 +51,15 @@
     public int getVersion()         { return version; }
 
 
-    /**
-     *  SOX format for these hashes is the PGP format with leading byte.
-     */
-    protected static final int MD_SHA1 = 2;
 
     protected DepositRequest depositRequest;
     protected byte[]         depositRequestData;
 
+    /**
+     *  The Exchange Payment is what is returned in case of a Withdrawal.
+     *  When in the receipt, it is a signed version of the Proto Payment
+     *  in the DepositRequest.
+     */
     protected AbstractPayment exPayment;
     protected byte[]          exPaymentData;
 
@@ -90,7 +103,25 @@
      *  replace it with a signed payment.
      */
     public AbstractPayment getExchangePayment()    { return exPayment; }
-    public void setExchangePayment(AbstractPayment p) { exPayment = p; }
+    public void setExchangePayment(AbstractPayment p)
+        throws SOXPacketException
+    {
+        exPayment = p;
+        ByteArrayOutputStream baos = new ByteArrayOutputStream();
+        try {
+             p.encode(baos);
+        } catch(IOException e) {
+            e.printStackTrace();
+            throw new SOXPacketException(e.getMessage());
+        }
+        exPaymentData = baos.toByteArray();
+    }
+    public void setExchangePayment(byte[] data)
+        throws SOXPacketException
+    {
+        exPaymentData = data;
+        exPayment = PaymentFactory.decode(data);
+    }
 
     /**
      * Get the DepositRequest object that was used to deposit the payment
@@ -180,18 +211,24 @@
     }
 
 
-    public boolean isValid()
+    private boolean isValid()
     {
         int errors = 0;
 
-        String    pid  = depositRequest.getPaymentId();
+// System.err.println("CHECK VALIDITY OF: " + toString() + "\n\n");
+
+        AbstractPayment pay = depositRequest.getPay();
+
+// System.err.println(" against these contents of Pay: " + pay);
+
+        String    pid  = pay.getId();
         String    did  = depositRequest.getDepositId();
         AccountId tgt  = depositRequest.getTarget();
         AccountId src  = depositRequest.getSource();
         ItemId    item = depositRequest.getItem();
         long      qty  = depositRequest.getQty();
 
-        if (!pid.equals(this.pid))
+        if (pay instanceof Payment && !pid.equals(this.pid))
         {
             logmsg("mismatch PID: "+pid+" != "+this.pid);
             errors++;
@@ -207,7 +244,7 @@
             logmsg("mismatch Target: "+tgt+" != "+this.tgt);
             errors++;
         }
-        if (!src.equals(this.src))
+        if (!src.equals(this.src) && !src.isOpen())
         {
             logmsg("mismatch Source: "+src+" != "+this.src);
             errors++;
@@ -217,6 +254,20 @@
             logmsg("mismatch Item: "+item+" != "+this.item);
             errors++;
         }
+        int id = item.getOpenPGPId();
+        if (id != Id.MD_SHA1)
+        {
+            logmsg("not a SHA hash: " + item + " (" + id + ")");
+            logmsg("payment is " + pay);
+            errors++;
+        }
+        byte[] b = item.getByteArray();
+        if ((b == null) || (b.length != 20))
+        {
+            logmsg("not a SHA len: " + item);
+            logmsg("payment is " + pay);
+            errors++;
+        }
         if (qty != this.qty)
         {
             logmsg("mismatch Item: "+qty+" != "+this.qty);
@@ -228,6 +279,7 @@
         {
             logmsg("Receipt bad: " + errors + " errors.");
         }
+// System.err.println("END CHECK VALIDITY");
         return 0 == errors;
     }
 
@@ -268,7 +320,7 @@
         this.depositRequest = req;
 
         if (!isValid())
-            throw new SOXPacketException("dud Receipt");
+            throw new SOXPacketException("dud Receipt: " + this);
 
         //
         //  Some receipts for this time had problems with the time.
@@ -426,47 +478,44 @@
         if (version >= RECEIPT_PROTO)
             flags = dis.readLong();
 
-        //
-        //  Perl code shoves a one byte (OpenPGP) Message Digest Type
-        //  in front of hashes.
-        //
-        int hash_type = dis.readUnsignedByte();
-        if (MD_SHA1 != hash_type)
-            throw new SOXPacketException(
-                        "Unknown Hash type: SHA=2 != " + hash_type);
-        item = new ItemId(dis);
+// System.err.println("Receipt decode started with " + vstring());
+
+        item = decodeItemId(dis);
         qty = dis.readLong();
+// System.err.println("Receipt qty " + qty + " of item " + item);
         timestamp = dis.readLong();
+// System.err.println("time " + timestamp);
 
         xid = readString(dis);
         pid = readString(dis);
         did = readString(dis);
+// System.err.println("xid " + xid + "   pid " + pid + "   did " + did);
 
-        hash_type = dis.readUnsignedByte();
-        if (MD_SHA1 != hash_type)
-            throw new SOXPacketException(
-                        "Unknown Hash type: SHA=2 != " + hash_type);
-        tgt = new AccountId(dis);
-        hash_type = dis.readUnsignedByte();
-        if (MD_SHA1 != hash_type)
-            throw new SOXPacketException(
-                        "Unknown Hash type: SHA=2 != " + hash_type);
-        src = new AccountId(dis);
+        tgt = decodeAccountId(dis);
+// System.err.println("decoded tgt: " + tgt);
+        src = decodeAccountId(dis);
+// System.err.println("decoded src: " + src);
 
         depositRequestData = readByteArray(dis);
+// System.err.println("deposit byte array len: " + depositRequestData.length);
         depositRequest = new DepositRequest(depositRequestData);
+// System.err.println("decoded deposit: " + depositRequest);
 
         exPaymentData = null;
         exPayment = null;
-        if (version >= RECEIPT_PROTO && isWithdrawalReceipt())
+        if ((version >= RECEIPT_PROTO) && isWithdrawalReceipt())
         {
             exPaymentData = readByteArray(dis);
-            exPayment = new Payment(exPaymentData);
+// System.err.println("decoding ProtoPay " + exPaymentData.length);
+            exPayment = PaymentFactory.decode(exPaymentData);
         }
 
+// System.err.println("reading sig");
         sig = readByteArray(dis);
 
+// System.err.println("getting HashSig");
         byte[] my_sig = getHashSig();
+// System.err.println("HashSig is " + webfunds.utils.Hex.printable(my_sig));
         
         if (!Utils.byteEquals(my_sig, sig))
         {
@@ -475,13 +524,59 @@
                 webfunds.utils.Hex.data2hex(sig) + " (=packet)");
         }
 
+// System.err.println("check validity:");
         if (!isValid())
         {
+// System.err.println("is bad validity:");
             throw new SOXPacketException("Dud Receipt");
         }
+// System.err.println("is good validity, PASSED!");
     }
 
     /**
+     *  Get SOX2 Hash for an ItemId, which has an
+     *  OpenPGP Message Digest Type in advance of it.
+     *  ItemId is only SHA.
+     */
+    protected ItemId decodeItemId(DataInputStream dis)
+        throws SOXPacketException, IOException
+    {
+        int hash_type = dis.readUnsignedByte();
+        if (
+               (Id.MD_SHA1 != hash_type)
+           )
+            throw new SOXPacketException(
+                        "Unknown Hash type: SHA=2 != " + hash_type);
+
+        ItemId hash = new ItemId(dis);
+        hash.setOpenPGPId(hash_type);
+        return hash;
+    }
+
+    /**
+     *  Get SOX2 Hash for an AccountId, which has an
+     *  OpenPGP Message Digest Type in advance of it.
+     *  This one can be NAH for token float accounts,
+     *  ones that are not directly accessible from
+     *  outside.
+     */
+    protected AccountId decodeAccountId(DataInputStream dis)
+        throws SOXPacketException, IOException
+    {
+        int hash_type = dis.readUnsignedByte();
+        if (
+               (Id.MD_SHA1 != hash_type) &&
+               (Id.MD_NAH != hash_type)
+           )
+            throw new SOXPacketException(
+                        "Unknown Hash type: NAH=110/SHA=2 != " + hash_type);
+
+        AccountId hash = new AccountId(dis);
+        hash.setOpenPGPId(hash_type);
+        return hash;
+    }
+
+    /**
      *  PRETEND Signature - the receipt is signed with a SHA1 MD,
      *  encoded as a message digest (type, data) within a string.
      *  Hmm, this gives us a problem when we start using DSA,
@@ -507,7 +602,7 @@
 
         int full_len = sig_array.length + 1;
         byte[] lam_sig = new byte[full_len];
-        lam_sig[0] = MD_SHA1;
+        lam_sig[0] = Id.MD_SHA1;
         for (int i = 1; i < full_len; i++)
              lam_sig[i] = sig_array[i-1];
         return lam_sig;
@@ -540,8 +635,9 @@
         if (version >= RECEIPT_PROTO)
             dos.writeLong(flags);
 
-        dos.writeByte(MD_SHA1);
-        item.encode(dos);
+        // dos.writeByte(Id.MD_SHA1);
+        // item.encode(dos);
+        encodeId(item, dos);
         dos.writeLong(qty);
         dos.writeLong(timestamp);
 
@@ -549,10 +645,12 @@
         writeString(dos, pid);
         writeString(dos, did);
 
-        dos.writeByte(MD_SHA1);
-        tgt.encode(dos);
-        dos.writeByte(MD_SHA1);
-        src.encode(dos);
+        encodeId(tgt, dos);
+        encodeId(src, dos);
+        // dos.writeByte(Id.MD_SHA1);
+        // tgt.encode(dos);
+        // dos.writeByte(Id.MD_SHA1);
+        // src.encode(dos);
 
         // written as array, as original might vary on encode()
         writeByteArray(dos, depositRequestData);
@@ -561,14 +659,32 @@
     }
 
     /**
-     * Encode a receipt, writing the data to an output stream,
-     * in a format suitable for sending to restoring using
-     * the decode() method to re-construct the object.
+     *  Encode a SOX2 hash, with OpenPGP id as a byte before it.
+     *  Used for hashes in the receipt body.
      *
-     * @param os the stream on which to send the output
-     * @return byte[] the receipt in encoded form
-     * @excep an I/O error occurred writing to the output stream
+     *  @param dos the stream on which to send the output
+     *  @return byte[] the receipt in encoded form
+     *  @excep an I/O error occurred writing to the output stream
      */
+    public void encodeId(Id id, DataOutputStream dos)
+        throws IOException
+    {
+        int openPGPid = id.getOpenPGPId();
+        if (openPGPid < 0)
+            throw new IOException("unknown hash type: " + openPGPid + "; "+id);
+        dos.writeByte(openPGPid);
+        id.encode(dos);
+    }
+
+    /**
+     *  Encode a receipt, writing the data to an output stream,
+     *  in a format suitable for sending to restoring using
+     *  the decode() method to re-construct the object.
+     *
+     *  @param os the stream on which to send the output
+     *  @return byte[] the receipt in encoded form
+     *  @excep an I/O error occurred writing to the output stream
+     */
     public void encode(OutputStream os)
         throws IOException
     {
@@ -647,17 +763,31 @@
      */
     public String toString()
     {
-        String retval = "SOX Receipt:\n";
+        String s = "SOX Receipt: " + vstring() + "\n";
 
-        retval += "\titem: "+item+"\tqty: "+qty+ "\n";
-        retval += "\txid: "+xid+"\tpid: "+pid+ "\tdid: "+did+ "\n";
-        retval += "\ttime: "+timestamp+"\tDesc: "+
+        s += "  item: "+item+" Q="+qty+ "\n";
+        s += "  xid: "+xid+" pid="+pid+ " did="+did+ "\n";
+        s += "  time: "+timestamp+"\tDesc: "+
                   webfunds.utils.Hex.printable(getPaymentDesc())+"\n";
-        retval += "\tsrc: "+src.fp()+"\ttgt: "+tgt.fp()+ "\n";
+        s += "  src: "+src.fp()+"\ttgt: "+tgt.fp()+ "\n";
+        s += "  DR: "+depositRequest+"\n";
+        if (isWithdrawalReceipt())
+            s += "  Withdraws: "+exPayment+"\n";
         if (sig != null)
-            retval += "\tSignature: "+sig+"\n";
+            s += "  Signature: "+sig+"\n";
+        s += "=========================== END Receipt\n";
 
-        return retval;
+        return s;
+    }
+
+    public String vstring()
+    {
+        String s = "V" + version;
+
+        if (version >= RECEIPT_PROTO)
+            s += " f=" + flags;
+
+        return s;
     }
 
     /** @return a receipt that is only mildly internally valid */
@@ -665,8 +795,6 @@
     {
         DepositRequest req = DepositRequest.example();
 
-//System.err.println("Payment: " + req.getPayment());
-
         AbstractPayment p = req.getPay();
         long from = p.getValidFrom() / 1000;
         from *= 1000;
@@ -676,7 +804,6 @@
         if (relTime < 0) relTime = -relTime;
         relTime = relTime % (till - from);
         long absTime = from + relTime;
-//System.err.println("  "+from+"   "+till+"    "+relTime+"   "+absTime);
 
         String xid = Utils.exampleString();
 
@@ -687,6 +814,15 @@
         ItemId    item = req.getItem();
         long      qty  = req.getQty();
 
+        /*
+         *  A receipt cannot be written to/from an open accountId,
+         *  but it can be to/from an internal Float account.
+         */
+        if (tgt.isOpen())
+            tgt = AccountId.example(Id.LEN_NAH);
+        if (src.isOpen())
+            src = AccountId.example(Id.LEN_NAH);
+
         Receipt receipt;
         try {
             receipt = new Receipt(
@@ -699,11 +835,29 @@
             throw new RuntimeException("SOXPEx: " + ex);
         }
 
+        /*
+         *  In the event of a withdrawal, the withdrawn payment
+         *  needs to be added to the Receipt, hopefully signed
+         *  by the Issuer.  in this case, we cheat and just shove
+         *  in the proto payment from the DR.
+         */
+        if (req.getTypeOfPayment() != PaymentFactory.NONE)  // wrong for SOX
+        {
+            receipt.version = RECEIPT_PROTO;
+            receipt.flags = WITHDRAW_PROTO;
+            AbstractPayment proto = req.getProto();
+            try {
+                receipt.setExchangePayment(proto);
+            } catch (SOXPacketException ex) {
+                throw new Panic("Proto: " + ex);
+            }
+        }
+
         try {
             receipt.setSignature(receipt.getHashSig());
         } catch (IOException ex) {
             ex.printStackTrace();
-            throw new RuntimeException("sig IOEx: " + ex);
+            throw new Panic("sig IOEx: " + ex);
         }
 
         return receipt ;
@@ -711,7 +865,7 @@
 
     public static void main(String[] args)
     {
-	int num = 200;
+	int num = 20000;
 	String type = "-c";
 	if (args.length > 0)
 	{
@@ -767,6 +921,7 @@
     protected static void cycle()
         throws Exception
     {
+// System.err.println("\n\n\n\n\n\n\n\n\n\n");
 	Receipt p = example();
         System.err.println("Writing: " + p);
         ByteArrayOutputStream baos = new ByteArrayOutputStream();