[Webfunds-commits] java/webfunds/sox/value StateReceiptStore.java StateReceipt.java StoreAccountStore.java ValueManager.java AccountStore.java ReceiptsStore.java

Ian Grigg iang@cypherpunks.ai
Sun, 25 Mar 2001 23:29:55 -0400 (AST)


iang        01/03/25 23:29:55

  Modified:    webfunds/sox AbstractPayment.java
               webfunds/sox/value StateReceipt.java StoreAccountStore.java
                        ValueManager.java
  Added:       webfunds/sox/value StateReceiptStore.java
  Removed:     webfunds/sox/value AccountStore.java ReceiptsStore.java
  Log:
  All compiled with:
  1.  StoreReceiptStore moved to StateReceiptStore and modified for only
      StateReceipts, so that Pending/Receipts are hidden
  2.  ValueManager now uses only StateReceipts
  3.  got rid of useless interfaces

Revision  Changes    Path
1.6       +3 -2      java/webfunds/sox/AbstractPayment.java

Index: AbstractPayment.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/AbstractPayment.java,v
retrieving revision 1.5
retrieving revision 1.6
diff -u -r1.5 -r1.6
--- AbstractPayment.java	2001/03/23 15:06:03	1.5
+++ AbstractPayment.java	2001/03/26 03:29:52	1.6
@@ -1,5 +1,5 @@
 /*
- * $Id: AbstractPayment.java,v 1.5 2001/03/23 15:06:03 iang Exp $
+ * $Id: AbstractPayment.java,v 1.6 2001/03/26 03:29:52 iang Exp $
  *
  * Copyright (c) Systemics Inc 1995-2000 on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -83,7 +83,8 @@
      *  Get the Payment Id.
      *  should be overridden for identified payments.
      */
-    public String getId() { return paymentId; }
+    public String getId() { return paymentId; }   // DEPRECATE
+    public String getPaymentId() { return paymentId; }
 
     /**
      * Get the date from which the payment is valid.



1.5       +60 -22    java/webfunds/sox/value/StateReceipt.java

Index: StateReceipt.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/value/StateReceipt.java,v
retrieving revision 1.4
retrieving revision 1.5
diff -u -r1.4 -r1.5
--- StateReceipt.java	2001/03/24 23:57:24	1.4
+++ StateReceipt.java	2001/03/26 03:29:53	1.5
@@ -1,17 +1,29 @@
 /*
- * $Id: StateReceipt.java,v 1.4 2001/03/24 23:57:24 iang Exp $
+ * $Id: StateReceipt.java,v 1.5 2001/03/26 03:29:53 iang Exp $
  *
  * Copyright (c) 2000 Systemics Inc on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
  */
 package webfunds.sox.value;
 
-import java.io.*;
-import java.security.*;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.IOException;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.ByteArrayOutputStream;
+// import java.security.*;
+
+import webfunds.utils.Panic;
+
+import webfunds.sox.Encodable;
+import webfunds.sox.Receipt;
+import webfunds.sox.AccountId;
+import webfunds.sox.ItemId;
+import webfunds.sox.SOXPacketException;
+import webfunds.sox.Utils;
 
-import webfunds.sox.*;
 
-
 /**
  *  This class represents a payment issued and maybe settled.
  *  If pending, only pending information is included.
@@ -39,7 +51,6 @@
     public boolean isCancelling()  { return (state == CANCELLING); }
     public boolean isCancelled()   { return (state == CANCELLED); }
     public boolean isSucceeded()   { return (state == SUCCEEDED); }
-
     public boolean isComplete()    { return isSucceeded() || isCancelled(); }
 
 
@@ -51,7 +62,7 @@
     protected PendingReceipt pendingReceipt;
 
     /**
-     *  Get the Pending if there.
+     *  Get the Receipt if there.
      *  @return the Receipt, else null if not settled as yet
      */
     public Receipt           getReceipt()         { return receipt; }
@@ -59,23 +70,52 @@
 
     public void  setReceipt(Receipt r)
     {
-        state = SUCCEEDED;
+        if (!pid.equals(r.getPaymentId()))
+            throw new Panic("not my Receipt: " + r);
+
+        if (receipt != null)
+        {
+            /*
+             *  We already have a Receipt.
+             *  As we are idempotent, just check that they are the same.
+             *  If they are, just return, the good works are already done.
+             */
+            if (!receipt.equals(r))
+                throw new Panic("Receipts not equiv: \n\n" + r +
+                                "\n\n" + receipt);
+            return ;
+        }
+
+        state = getState(r);
         receipt = r;
         pendingReceipt = null;
     }
 
+    /* what is the point of this?  it is an application choice, not here */
+    /*
     public void  setCancelled(Receipt r)
     {
-        state = CANCELLED;
+        state = r.getState(r);
         receipt = r;
         pendingReceipt = null;
     }
+    */
 
     public void  setCancelling()
     {
         state = CANCELLING;
     }
 
+    protected static int getState( Receipt rec )
+    {
+        AccountId tgt = rec.getTarget();
+        AccountId src = rec.getSource();
+        if (rec.getQty() == 0 && tgt.equals(src))
+            return CANCELLED;
+        else
+            return SUCCEEDED;
+    }
+
 
 
     /**
@@ -94,11 +134,9 @@
      * Create a new StateReceipt.
      * This constructor used when making a Payment committment.
      *
-     * @param pendingReceipt the deposit request for which this is the receipt
-     * @param transactionId the transaction identifier
-     * @param date the time of the transaction
+     * @param pr the PendingReceipt indicating the committment
      */
-    public StateReceipt( PendingReceipt pr )
+    /* family */ StateReceipt( PendingReceipt pr )
     {
         this.pendingReceipt = pr;
         this.receipt = null;
@@ -109,18 +147,16 @@
 
     /**
      * Create a new StateReceipt.
-     * This constructor used when making a Payment committment.
+     * This constructor used when transaction already complete.
      *
-     * @param pendingReceipt the deposit request for which this is the receipt
-     * @param transactionId the transaction identifier
-     * @param date the time of the transaction
+     * @param rec the Receipt received from the Issuance Server
      */
     /* family */ StateReceipt( Receipt rec )
     {
         this.pendingReceipt = null;
         this.receipt = rec;
 
-        this.pid = rec.getPaymentId();
+        this.pid = rec.getPaymentId();      // my unique id
 
         AccountId tgt = rec.getTarget();
         AccountId src = rec.getSource();
@@ -130,6 +166,10 @@
             this.state = SUCCEEDED;
     }
 
+
+
+////////////  Encode & Decode  /////////////////////////////////////
+
     /**
      * Construct the object from a byte array
      * that was previously returned from the encode()
@@ -266,11 +306,9 @@
         StateReceipt obj = new StateReceipt(pending);
 
         int state = Utils.exampleByte() & 0x03;
+
+        obj.setReceipt(Receipt.example());
 
-        if (state == SUCCEEDED)
-            obj.setReceipt(Receipt.example());
-        if (state == CANCELLED)
-            obj.setCancelled(Receipt.example());
         if (state == CANCELLING)
             obj.setCancelling();
 



1.25      +15 -29    java/webfunds/sox/value/StoreAccountStore.java

Index: StoreAccountStore.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/value/StoreAccountStore.java,v
retrieving revision 1.24
retrieving revision 1.25
diff -u -r1.24 -r1.25
--- StoreAccountStore.java	2001/03/24 23:57:24	1.24
+++ StoreAccountStore.java	2001/03/26 03:29:54	1.25
@@ -1,5 +1,5 @@
 /*
- * $Id: StoreAccountStore.java,v 1.24 2001/03/24 23:57:24 iang Exp $
+ * $Id: StoreAccountStore.java,v 1.25 2001/03/26 03:29:54 iang Exp $
  *
  * Copyright (c) Systemics Ltd 1995-1999 on behalf of
  * the WebFunds Development Team.  All Rights Reserved.
@@ -11,18 +11,23 @@
 
 import webfunds.utils.Debug;
 import webfunds.utils.Hex;
-import webfunds.sox.*;
-import webfunds.client.*;
+import webfunds.utils.Panic;
+
+import webfunds.sox.Account;
+import webfunds.sox.AccountId;
+import webfunds.sox.SOXPacketException;
+
 import webfunds.store.Store;
 import webfunds.store.StoreException;
 
 
 public class StoreAccountStore
-    extends Debug implements AccountStore
+    extends Debug
 {
+    static final String ACCOUNTS_NAME = "Accounts";  // a standard name
+
     
     Store        store;
-//    IssuerFinder finder;
     
     /**
      *  Store for accounts.
@@ -33,49 +38,31 @@
         if (store == null)
             throw new IllegalArgumentException("StoreAccountStore store==null");
         this.store = store;        
-//        this.finder = null;        
-//logmsg("SAS " + this.getClass().getClassLoader());
-    }
-    
-    /**
-     *  Store for accounts.
-     *  @option finder is set in all accounts that are requested.
-    public StoreAccountStore(Store store, IssuerFinder finder)
-    {
-        if (store == null)
-            throw new IllegalArgumentException("StoreAccountStore store==null");
-        this.store  = store;        
-        this.finder = finder;
     }
-     */
     
         
     public Account[] getAllAccounts()
-        // throws StoreException
+        throws StoreException
     {
         Account[] retval = new Account[store.size()];
-//logmsg("there are " + retval.length + " accounts in store...");
-//logmsg(" CL " + this.getClass().getClassLoader());
         int i = 0;
         try
         {
             for (Enumeration e = store.elements();e.hasMoreElements(); i++)
             {
-//logmsg("    get: " + i);
                 retval[i] = new Account((byte[])e.nextElement());
-//logmsg("    got: " + i);
             }
         }
         catch (SOXPacketException ex)
         {
-            ex.printStackTrace(err());
-            System.exit(1);
-            // throw new StoreException("Couldn't decode data");
+            throw new Panic(" lost accounts?  " + ex);
         }
         return retval;
     }
     
-    // idempotent, saves changes.
+    /**
+     *  idempotent, saves changes.
+     */
     public void addAccount(Account acct)
         throws StoreException
     {
@@ -153,7 +140,6 @@
              throw new StoreException("Unknown object from " + key + ": " +
                                       obj.getClass().getName());
 
-        // ac.setFinder(finder);
         return ac;
     }
     



1.2       +38 -41    java/webfunds/sox/value/ValueManager.java

Index: ValueManager.java
===================================================================
RCS file: /home/webfunds/cvsroot/java/webfunds/sox/value/ValueManager.java,v
retrieving revision 1.1
retrieving revision 1.2
diff -u -r1.1 -r1.2
--- ValueManager.java	2001/03/24 23:57:24	1.1
+++ ValueManager.java	2001/03/26 03:29:54	1.2
@@ -1,4 +1,4 @@
-/* $Id: ValueManager.java,v 1.1 2001/03/24 23:57:24 iang Exp $
+/* $Id: ValueManager.java,v 1.2 2001/03/26 03:29:54 iang Exp $
  *
  * Copyright (c) Systemics Inc. 1995-2000 on behalf of
  * The WebFunds Development Team.  All Rights Reserved.
@@ -60,7 +60,7 @@
  *
  *  (Was the SOXWallet -- attempt to separate and make simple!)
  *
- * @version $Revision: 1.1 $
+ * @version $Revision: 1.2 $
  */
 public class ValueManager
     extends Debug
@@ -71,9 +71,9 @@
      */
     protected   Store                   store;
 
-    protected   AccountStore            accountStore;
+    protected   StoreAccountStore       accountStore;
     protected   SOXServerStore          soxes;
-    protected   ReceiptsStore           receiptStore;
+    protected   StateReceiptStore       receiptStore;
     protected   Properties              properties;
     protected   IssuerFinder            finder;
 
@@ -163,24 +163,28 @@
         //  Account store.
         //  Failures here are unrecoverable.
         //
-            logmsg("Trying to get 'Accounts' store");
-            Store st = store.getStore("Accounts");
-            logmsg(
+        logmsg("Trying to get 'Accounts' store");
+        Store st = store.getStore(StoreAccountStore.ACCOUNTS_NAME);
+        logmsg(
                 "  We got: " + st +
                 ", with size: " + st.size() );
-            StoreAccountStore accs;
-            accs = new StoreAccountStore(st);
-            accs.debug(bug);
+        // StoreAccountStore accs;
+        accountStore = new StoreAccountStore(st);
+        accountStore.debug(bug);
 
-            accountStore = (AccountStore)accs;
+        //accountStore = (AccountStore)accs;
 
         //
         //  Receipt store.
         //  Failures here are unrecoverable.
         //
-            receiptStore = new StoreReceiptStore(store.getStore("Receipts"));
-            //receiptStore.debug(bug);
+        Store s = store.getStore(StateReceiptStore.RECEIPTS_NAME);
 
+        receiptStore = new StateReceiptStore(s);
+        receiptStore.debug(bug);
+
+        // not an issue currently: migrate();
+
         //
         //  An old store is Issuers.  We should remove that some time.
         //
@@ -438,7 +442,7 @@
         {
             StateReceipt sr;
             try {
-                sr = receiptStore.getReceipt(src, item, pid);
+                sr = receiptStore.getStateReceipt(src, item, pid);
             } catch (StoreException ex) {
                 throw new webfunds.utils.Panic("get StateReceipt for " + pid);
             }
@@ -501,8 +505,10 @@
                                           pay.getQty(),
                                           pay.getDesc(),
                                           new Date());
+        StateReceipt sr = new StateReceipt(pending);
+
         try {
-            receiptStore.addPendingReceipt(pending, src);
+            receiptStore.addStateReceipt(sr, src);
         } catch (StoreException ex) {
             ex.printStackTrace();
             throw new PaymentException(PaymentException.UNKNOWN,
@@ -983,7 +989,7 @@
     {
         StateReceipt sr;
         try {
-            sr = receiptStore.getReceipt(acct, item, id);
+            sr = receiptStore.getStateReceipt(acct, item, id);
         } catch (StoreException ex) {
             throw new webfunds.utils.Panic("get StateReceipt for " + id);
         }
@@ -1012,7 +1018,7 @@
             String pid = pids[i];
             StateReceipt sr;
             try {
-                sr = receiptStore.getReceipt(acct, item, pid);
+                sr = receiptStore.getStateReceipt(acct, item, pid);
             } catch (StoreException ex) {
                 storex = ex ;
                 errors += "\nStoreEx: " + ex;
@@ -1055,7 +1061,7 @@
             }
 
             // hey, is this necessary?  It gets updated later anyway.
-            sr.setCancelled(receipt);
+            // sr.setReceipt(receipt);
             // put new sr!
 
             for (int j = 0; j < mails.length; j++)
@@ -1103,7 +1109,7 @@
 
         StateReceipt sr;
         try {
-            sr = receiptStore.getReceipt(acct, item, pid);
+            sr = receiptStore.getStateReceipt(acct, item, pid);
         } catch (StoreException ex) {
             throw new CancelException(CancelException.INTERNAL,ex.getMessage());
         }
@@ -1615,8 +1621,19 @@
 
     protected boolean handleReceipt(Receipt rec, AccountId acct)
     {
+
+        ItemId item = rec.getItem();
+        String pid = rec.getPaymentId();
+
         try {
-            receiptStore.addReceipt(rec, acct);
+            StateReceipt sr = receiptStore.getStateReceipt(acct, item, pid);
+
+            if (sr == null)
+                sr = new StateReceipt(rec);
+            else
+                sr.setReceipt(rec);
+
+            receiptStore.addStateReceipt(sr, acct);
         } catch (StoreException ex) {
             ex.printStackTrace();
             logmsg("losing receipts for " + acct.fp() + ": " + ex);
@@ -1645,14 +1662,14 @@
      *
      *  @return a list of Receipt within this subaccount
      */
-    public Receipt[] getReceipts(AccountId acct, ItemId item)
+    public StateReceipt[] getReceipts(AccountId acct, ItemId item)
         throws AccountException
     {
-        Receipt[] receipts;
+        StateReceipt[] receipts;
 
         try
         {
-            receipts = receiptStore.getReceipts(acct, item);
+            receipts = receiptStore.getStateReceipts(acct, item);
         }
         catch (StoreException ex)
         {
@@ -1662,26 +1679,6 @@
         return receipts;
     }
 
-    /**
-     *
-     *  @return a list of Pendings within this subaccount
-     */
-    public PendingReceipt[] getPending(AccountId acct, ItemId item)
-        throws AccountException
-    {
-        PendingReceipt[] pendings;
-
-        try
-        {
-            pendings = receiptStore.getPendingReceipts(acct, item);
-        }
-        catch (StoreException ex)
-        {
-            throw new AccountException("no pendings: " + ex);
-        }
-
-        return pendings;
-    }
 
 
     public String toString()



1.1                  java/webfunds/sox/value/StateReceiptStore.java

Index: StateReceiptStore.java
===================================================================
/*
 * $Id: StateReceiptStore.java,v 1.1 2001/03/26 03:29:53 iang Exp $
 *
 * Copyright (c) Systemics Ltd 1999 on behalf of
 * the WebFunds Development Team.  All Rights Reserved.
 */
package webfunds.sox.value;

import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Vector;

import java.io.IOException;
import java.io.PrintWriter;
import java.io.ByteArrayOutputStream;

import webfunds.utils.Hex;
import webfunds.utils.Debug;
import webfunds.utils.Panic;

import webfunds.sox.Receipt;
import webfunds.sox.AccountId;
import webfunds.sox.ItemId;
import webfunds.sox.SOXPacketException;

import webfunds.store.Store;
import webfunds.store.StoreException;


/**
 *  A place to persistantly store SOX receipts.
 *
 *  Data-compatible with StoreReceiptStore, but distinct API.
 *
 *  It has persistant stores, one each for pending and confirmed receipts.
 *  This is a kludge.
 */
public final class StateReceiptStore
    extends Debug
{

    Store store;
    static final String RECEIPTS_NAME = "Receipts";
    static final String PENDING = "Pending";

    public StateReceiptStore(Store store, PrintWriter bug)
    {
        debug(bug);
        init(store);
    }

    public StateReceiptStore(Store store)
    {
        init(store);
    }

    protected void init(Store store)
    {
        this.store = store;
    }



//////////////  Store Manipulation  //////////////////////////

    /**
     * Get the sub account store.
     */
    protected Store getSub(AccountId acct, ItemId item)
        throws StoreException
    {
        String acctName = Hex.data2hex(acct.getByteArray());
        String subName = Hex.data2hex(item.getByteArray());
        Store sub;
        sub = store.getStore(acctName).getStore(subName);
        return sub ;
    }

    /**
     * Get the Pending store from the sub account store.
     */
    protected Store getPen(Store sub)
        throws StoreException
    {
        return sub.getStore(PENDING);
    }

    /**
     * Get the Pending store from the account details.
     */
    protected Store getPen(AccountId acct, ItemId item)
        throws StoreException
    {
        return getPen(getSub(acct, item));
    }



//////////////  Receipts  ///////////////////////////////////////


    /**
     * Add this signed receipt into the store.
     * @except StoreException if for any reason the receipt doesn't get added
     */
    protected void addReceipt(Store sub, Receipt receipt)
        throws StoreException
    {
        //
        // Encode it.
        //
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            receipt.encode(baos);
        } catch (IOException ex) {
            throw new StoreException("dud receipt: " + ex);
        }

        //
        //  Is this right?  We should be storing according to
        //  our id, not the issuer id?
        //
        String tid = receipt.getTransactionId();
        logmsg("storing receipt " + tid);
        // xid is only unique within issuer.  Lucky this is a subaccount
        sub.put(tid, baos.toByteArray());

        if (store.checkErrors() )
            throw new StoreException("Couldn't add receipt: " + receipt);
    }

    /**
     * A signed receipt has arrived.
     * Add it to the normal store, then drop any applicable pending.
     * Idempotent.  Also used by convert().
     */
    protected void addReceipt(Receipt receipt, AccountId acct)
        throws StoreException
    {
        //  I think there is a bug here somewhere.  A receipt
        //  was not stored but was signed for :(  P956094425072 / xid: 46748
        ItemId item = receipt.getItem();
        logmsg("getting sub " + acct + " / " + item);
        Store sub = getSub(acct, item);

        addReceipt(sub, receipt);

        //
        // Remove the pending.
        //
        String pid = receipt.getPaymentId();
        removePending(pid, acct, item);

    }

    /**
     *  Get a list of receipts for a subaccount (acct / item).
     */
    protected Receipt[] getReceipts(AccountId acct, ItemId item)
        throws StoreException
    {
        Store sub = getSub(acct, item);

        Receipt[] retval = new Receipt[sub.size() ];
        int i = 0;
        for (Enumeration e = sub.keys(); e.hasMoreElements(); i++)
        {
            String key = (String)e.nextElement();
            if (PENDING.equals(key))
                continue ;

            byte[] data = (byte[])sub.get(key);
            try {
                retval[i] = new Receipt(data);
            } catch (SOXPacketException pex) {
                System.err.println(pex.getMessage());
                throw new StoreException("Couldn't get file");
            }
        }

        if (store.checkErrors())
        {
            throw new StoreException("Errors in store");
        }
        return retval;
    }



//////////////  StateReceipts  ///////////////////////////////////////

    /**
     * A StateReceipt has arrived, add it in.
     * 
     * Idempotent.  Also used by convert().
     */
    public void addStateReceipt(StateReceipt sr, AccountId acct)
        throws StoreException
    {
        Receipt rec = sr.getReceipt();
        if (rec == null)
        {
            PendingReceipt pr = sr.getPendingReceipt();
            if (pr == null)
                throw new Panic("no PR nor R in SR: " + sr);
            addPendingReceipt(pr, acct);
        }
        else
        {
            addReceipt(rec, acct);
        }
    }


    /**
     *  Get a receipts for (acct / item / pid).
     *
     *  XXX: stored indexed with xid not pid, needs to search
     *
     *  @return the StateReceipt for that pid, else null if not found
     */
    protected StateReceipt getStateReceipt(AccountId acct, ItemId item, String pid)
        throws StoreException
    {
        Store sub = getSub(acct, item);

        for (Enumeration e = sub.keys(); e.hasMoreElements(); )
        {
            String key = (String)e.nextElement();
            if (PENDING.equals(key))
                continue ;

            byte[] data = (byte[])sub.get(key);
            Receipt r;
            try {
                r = new Receipt(data);
            } catch (SOXPacketException pex) {
                throw new StoreException("Could not get receipt: " + pex);
            }

            if (r.getPaymentId().equals(pid))
            {
                return new StateReceipt(r);
            }
        }

        Store pen = getPen(acct, item);
        for (Enumeration e = pen.keys(); e.hasMoreElements(); )
        {
            String key = (String)e.nextElement();
            if (PENDING.equals(key))
                continue ;

            byte[] data = (byte[])sub.get(key);
            PendingReceipt p;
            try {
                p = new PendingReceipt(data);
            } catch (SOXPacketException pex) {
                throw new StoreException("Could not get receipt: " + pex);
            }

            if (p.getPaymentId().equals(pid))
            {
                return new StateReceipt(p);
            }
        }

        if (store.checkErrors())
        {
            throw new StoreException("Errors in store");
        }
        return null;
    }

    /**
     *  Get a list of receipts for a subaccount (acct / item).
     */
    public StateReceipt[] getStateReceipts(AccountId acct, ItemId item)
        throws StoreException
    {
        Store sub = getSub(acct, item);

        Vector v = new Vector();
        for (Enumeration e = sub.keys(); e.hasMoreElements();)
        {
            String key = (String)e.nextElement();
            if (PENDING.equals(key))
                continue ;

            byte[] data = (byte[])sub.get(key);
            Receipt r;
            try {
                r = new Receipt(data);
            } catch (SOXPacketException pex) {
                throw new StoreException("Failed to recover Receipt: " + pex);
            }
            StateReceipt sr = new StateReceipt(r);
            v.addElement(sr);
        }

        Store pen = getPen(acct, item);

        for (Enumeration e = pen.elements(); e.hasMoreElements();)
        {
            PendingReceipt pr;
            try {
                pr = new PendingReceipt((byte[])e.nextElement());
            } catch(SOXPacketException pex) {
                throw new StoreException("Pending failed decode: " + pex);
            }
            StateReceipt sr = new StateReceipt(pr);
            v.addElement(sr);
        }

        StateReceipt[] receipts = new StateReceipt[v.size()];
        v.copyInto(receipts);

        return receipts;
    }


//////////////  PendingReceipts  ////////////////////////////////////

    /**
     * A payment has been made/signed, indicating a receipt will
     * come at some stage.  Store the receipt as pending.
     * Pending receipts are only for payments we write, not for deposits.
     */
    protected void addPendingReceipt(PendingReceipt pending, AccountId acct)
        throws StoreException
    {
        Store pen = getPen(acct, pending.item);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
            pending.encode(baos);
        } catch (IOException ex) {
            throw new StoreException("dud pending: " + ex);
        }
        pen.put(pending.transid, baos.toByteArray());

        if (store.checkErrors())
        {
            throw new StoreException("Errors in store");
        }
    }

    /**
     * Remove a pending receipt, presumably because it failed or
     * or it has moved to the normal store.
     * Was public but there is no reason for this.
     */
    protected void removePending(String id, AccountId acct, ItemId item)
        throws StoreException
    {
        Store pen = getPen(acct, item);

//System.err.println("REMOVE: " + id + " ( " + acct + " / " + item + " )");
        pen.remove(id);
        if (store.checkErrors())
        {
            throw new StoreException("Errors in store");
        }
    }

    /**
     * Return a list of pending receipts for a subaccount.
     */
    protected PendingReceipt[] getPendingReceipts(AccountId acct, ItemId item)
        throws StoreException
    {
        Store pen = getPen(acct, item);

        PendingReceipt[] retval = new PendingReceipt[pen.size()];
        int i = 0;
        PendingReceipt pend;
        for (Enumeration e = pen.elements(); e.hasMoreElements(); i++)
        {
            try {
                pend = new PendingReceipt((byte[])e.nextElement());
            } catch(SOXPacketException pex) {
                throw new StoreException("Pending failed decode: " + pex);
            }
            retval[i] = pend;
        }

        if (store.checkErrors() )
        {
            throw new StoreException("Couldn't get file");
        }
        return retval;
    }



////////////////////////////////////////////////////////////////////
//
//  Get Values
//

    /**
     * Does not check the subaccounts?  Not Needed?
     * @return true if the contents of the account's store have changed.
     */
    protected boolean isChanged(AccountId acct)
    {
        String acctName = Hex.data2hex(acct.getByteArray());
        Store thisstore;
        try {
            thisstore = store.getStore(acctName);
        } catch (StoreException ex) {
            return true;
        }

        // Store pendstore = pendingstore.getStore(acctName);
        return ( thisstore.isChanged() ) ; //|| pendstore.isChanged() );
    }

    /**
     * Used to see if a re-count of value is needed.
     * @return true if the contents of the SubAccount's store have changed.
     */
    protected boolean isChanged(AccountId acct, ItemId item)
    {
        Store sub;
        Store pen;
        try {
            sub = getSub(acct, item);
            pen = getPen(acct, item);
        } catch (StoreException ex) {
            return true;
        }
        return ( sub.isChanged() || pen.isChanged() );
    }

    Hashtable pendvaluecache = null;
    Hashtable confvaluecache = null;

    /**
     * What this returns is peculiar.
     * It is the sum of payments going out, so all pending
     * committments made, but not received.
     * As it is a committment out, it should be negative.
     */
    public long pendingValue(AccountId acct, ItemId item)
        throws StoreException
    {
        Store pen;
        pen = getPen(acct, item);

        if (pendvaluecache == null)
            pendvaluecache = new Hashtable();
        else if (!pen.isChanged())
        {
            //
            // used cached values if the store hasn't changed.
            //
            Object obj = null;
            Hashtable temp = (Hashtable)pendvaluecache.get(acct);
            if (temp != null)
                obj = temp.get(item);
            if (obj != null)
                 return ((Long)obj).longValue();
        }

        //
        // No cached value, or it is out of date.
        // Got to count them up again.
        //
        PendingReceipt[] pendings = null;
        pendings = getPendingReceipts(acct, item);

        long amount = 0;
        PendingReceipt pending = null;
        for (int i = 0; i < pendings.length; i++)
        {
            pending = pendings[i];
            AccountId src = pending.src;
            AccountId tgt = pending.tgt;
            boolean fromMe = acct.equals(src);
            boolean toMe   = acct.equals(tgt);

            if (fromMe)            // from this account
            {
                if (!toMe)         // to someone else
                    amount -= pending.amount;
            }
            else if (toMe)         // to this account
                { }                // ignore, not useful as pending
            else
                logmsg("huh? not " + acct + "'s pending: " +
                       pending + "\n( " + src + " ==> " + tgt + " )");
        }

        //
        //  Now cache the amount.
        //
        Long value = new Long(amount);
        Hashtable temp = (Hashtable)pendvaluecache.get(acct);
        if (temp == null)
        {
            temp = new Hashtable();
            pendvaluecache.put(acct, temp);
        }
        temp.put(item, value);
        return amount;
    }


    public long confirmedValue(AccountId acct, ItemId item)
        throws StoreException
    {
        Store sub;
        sub = getSub(acct, item);

        if (confvaluecache == null)
            confvaluecache = new Hashtable();
        else if (!sub.isChanged())
        {
            //
            // used cached values if the store hasn't changed.
            //
            Object obj = null;
            Hashtable temp = (Hashtable)confvaluecache.get(acct);
            if (temp != null)
                obj = temp.get(item);
            if (obj != null)
                 return ((Long)obj).longValue();
        }

        //
        // No cached value, or it is out of date.
        // Got to count them up again.
        //
        Receipt[] receipts;
        receipts = getReceipts(acct, item);

        long amount = 0;
        for (int i = 0; i < receipts.length; i++)
        {
            Receipt receipt = receipts[i];
            boolean fromMe = acct.equals(receipt.getSource());
            boolean toMe   = acct.equals(receipt.getTarget());

            //
            // if an internal transfer, both are true!
            //
            if (fromMe)
                amount -= receipt.getQty();
            if (toMe)
                amount += receipt.getQty();

            if (!fromMe && !toMe)
                logmsg("huh? not " + acct + "'s receipt: " +
                       receipt);
        }

        //
        //  Now cache the amount.
        //
        Long value = new Long(amount);
        Hashtable temp = (Hashtable)confvaluecache.get(acct);
        if (temp == null)
        {
            temp = new Hashtable();
            confvaluecache.put(acct, temp);
        }
        temp.put(item, value);

        return amount;
    }


}