FileDocCategorySizeDatePackage
DHTPluginStorageManager.javaAPI DocAzureus 3.0.3.442307Thu Nov 16 13:41:18 GMT 2006com.aelitis.azureus.plugins.dht.impl

DHTPluginStorageManager

public class DHTPluginStorageManager extends Object implements com.aelitis.azureus.core.dht.DHTStorageAdapter
author
parg

Fields Summary
private static final String
pub_exp
private static final String
modulus
private static final long
ADDRESS_EXPIRY
private static final int
DIV_WIDTH
private static final int
DIV_FRAG_GET_SIZE
private static final long
DIV_EXPIRY_MIN
private static final long
DIV_EXPIRY_RAND
private static final long
KEY_BLOCK_TIMEOUT_SECS
public static final int
LOCAL_DIVERSIFICATION_SIZE_LIMIT
public static final int
LOCAL_DIVERSIFICATION_ENTRIES_LIMIT
public static final int
LOCAL_DIVERSIFICATION_READS_PER_MIN_SAMPLES
public static final int
LOCAL_DIVERSIFICATION_READS_PER_MIN
public static final int
MAX_STORAGE_KEYS
private int
network
private com.aelitis.azureus.core.dht.DHTLogger
log
private File
data_dir
private AEMonitor
address_mon
private AEMonitor
contact_mon
private AEMonitor
storage_mon
private AEMonitor
version_mon
private AEMonitor
key_block_mon
private Map
version_map
private Map
recent_addresses
private Map
remote_diversifications
private Map
local_storage_keys
private volatile ByteArrayHashMap
key_block_map_cow
private volatile com.aelitis.azureus.core.dht.DHTStorageBlock[]
key_blocks_direct_cow
private com.aelitis.azureus.core.util.bloom.BloomFilter
kb_verify_fail_bloom
private long
kb_verify_fail_bloom_create_time
private static RSAPublicKey
key_block_public_key
Constructors Summary
public DHTPluginStorageManager(int _network, com.aelitis.azureus.core.dht.DHTLogger _log, File _data_dir)

	
	
		try{
			KeyFactory key_factory = KeyFactory.getInstance("RSA");
			
			RSAPublicKeySpec 	public_key_spec = 
				new RSAPublicKeySpec( new BigInteger(modulus,16), new BigInteger(pub_exp,16));
	
			key_block_public_key 	= (RSAPublicKey)key_factory.generatePublic( public_key_spec );

		}catch( Throwable e ){
			
			Debug.printStackTrace(e);
		}
		
		network		= _network;
		log			= _log;
		data_dir	= _data_dir;
		
		FileUtil.mkdirs(data_dir);
		
		readRecentAddresses();
		
		readDiversifications();
		
		readVersionData();
		
		readKeyBlocks();
	
Methods Summary
protected com.aelitis.azureus.core.dht.DHTStorageBlock[]buildKeyBlockDetails(ByteArrayHashMap map)

		List	kbs = map.values();
		
		Iterator	it = kbs.iterator();
		
		while( it.hasNext()){
			
			keyBlock	kb = (keyBlock)it.next();
			
			if ( !kb.isDirect()){
				
				it.remove();
			}
		}
		
		DHTStorageBlock[]	new_blocks = new DHTStorageBlock[kbs.size()];
		
		kbs.toArray( new_blocks );
		
		return( new_blocks );
	
protected com.aelitis.azureus.plugins.dht.impl.DHTPluginStorageManager$diversificationcreateDiversification(HashWrapper wrapper, byte type)

		diversification	div = new diversification( this, wrapper, type );
			
		remote_diversifications.put( wrapper, div );
		
		writeDiversifications();
		
		return( div );
	
public byte[][]createNewDiversification(com.aelitis.azureus.core.dht.transport.DHTTransportContact cause, byte[] key, boolean put_operation, byte diversification_type, boolean exhaustive)

		//System.out.println( "DHT create new diversification: put = " + put_operation +", type = " + diversification_type );
		
		HashWrapper	wrapper = new HashWrapper( key );
		
		try{
			storage_mon.enter();
		
			diversification	div = lookupDiversification( wrapper );
		
			boolean	created = false;
			
			if ( div == null ){
				
				div = createDiversification( wrapper, diversification_type );
				
				created	= true;			
			}
		
			byte[][] res = followDivChain( wrapper, put_operation, exhaustive );
		
			String	trace = "";
			
			for (int i=0;i<res.length;i++){
				
				trace += (i==0?"":",") + DHTLog.getString2( res[i] );
			}
			
			log.log( "SM: create div: " + DHTLog.getString2(key) + 
						", new = " + created + ", put = " + put_operation + 
						", exh = " + exhaustive + 
						", type = " + DHT.DT_STRINGS[diversification_type] + " -> " + trace +
						", cause = " + (cause==null?"<unknown>":cause.getString()));
			

			return( res );
			
		}finally{
			
			storage_mon.exit();
		}
	
protected voiddeleteStorageKey(com.aelitis.azureus.plugins.dht.impl.DHTPluginStorageManager$storageKey key)

		if ( local_storage_keys.remove( key ) != null ){
		
			if ( key.getDiversificationType() != DHT.DT_NONE ){
				
				writeDiversifications();
			}
		}
	
public com.aelitis.azureus.core.dht.DHTStorageKeyStatsdeserialiseStats(java.io.DataInputStream is)

		
		byte	version 	= is.readByte();
		
		final int	entry_count = is.readInt();
		final int	size		= is.readInt();
		final int	reads		= is.readInt();
		final byte	div			= is.readByte();

		return( 
			new DHTStorageKeyStats()
			{
				public int
				getEntryCount()
				{
					return( entry_count );
				}
				
				public int
				getSize()
				{
					return( size );
				}
				
				public int
				getReadsPerMinute()
				{
					return( reads );
				}
				
				public byte
				getDiversification()
				{
					return( div );
				}
			});
	
protected voidexportContacts(com.aelitis.azureus.core.dht.DHT dht)

		try{
			contact_mon.enter();
						
			File	saving = new File( data_dir, "contacts.saving" );
			File	target = new File( data_dir, "contacts.dat" );

			saving.delete();
			
			DataOutputStream	dos	= null;
			
			boolean	ok = false;
			
			try{
				FileOutputStream fos = new FileOutputStream( saving );
					
				dos = new DataOutputStream(fos);
					
				dht.exportState( dos, 32 );
					
				dos.flush();
					
				fos.getFD().sync();
			
				ok	= true;
				
			}finally{
				
				if ( dos != null ){
					
					dos.close();
					
					if ( ok ){
						
						target.delete();
						
						saving.renameTo( target );
					}
				}
			}
		}catch( Throwable e ){
			
			Debug.printStackTrace( e );
			
		}finally{
			
			contact_mon.exit();
		}
		
			// this is a good point to save diversifications - useful when they've expired
			// as writing isn't triggered at expiry time 
		
		writeDiversifications();
	
protected byte[][]followDivChain(HashWrapper wrapper, boolean put_operation, boolean exhaustive)

		List	list = new ArrayList();
		
		list.add( wrapper );
		
		list	= followDivChain( list, put_operation, 0, exhaustive, new ArrayList());
		
		byte[][]	res = new byte[list.size()][];
		
		for (int i=0;i<list.size();i++){
			
			res[i] = ((HashWrapper)list.get(i)).getBytes();
		}
		
		return( res );
	
protected java.util.ListfollowDivChain(java.util.List list_in, boolean put_operation, int depth, boolean exhaustive, java.util.List keys_done)

		List	list_out = new ArrayList();
	
		/*
		String	indent = "";
		for(int i=0;i<depth;i++){
			indent+= "  ";
		}
		System.out.println( indent + "->" );
		*/
		
			// for each entry, if there are no diversifications then we just return the value
			// for those with divs we replace their entry with the diversified set (which can
			// include the entry itself under some circumstances )
		
		for (int i=0;i<list_in.size();i++){
			
			HashWrapper	wrapper = (HashWrapper)list_in.get(i);
		
			diversification	div = lookupDiversification( wrapper );

			if ( div == null ){
				
				if ( !list_out.contains( wrapper )){
					
					list_out.add(wrapper);
				}
				
			}else{
				
				if ( keys_done.contains( wrapper )){
					
						// we've recursed on the key, this means that a prior diversification wanted
						// the key included, so include it now
					
					if ( !list_out.contains( wrapper )){
						
						list_out.add(wrapper);
					}
					
					continue;
				}
				
				keys_done.add( wrapper );
				
					// replace this entry with the diversified keys 
				
				List	new_list = followDivChain( div.getKeys( put_operation, exhaustive ), put_operation, depth+1, exhaustive, keys_done );
				
				for (int j=0;j<new_list.size();j++){
					
					Object	entry =  new_list.get(j);
					
					if ( !list_out.contains( entry )){
						
						list_out.add(entry);
					}
				}
			}
		}
		// System.out.println( indent + "<-" );

		return( list_out );
	
protected static java.lang.StringformatExpiry(long l)

		long	diff = l - SystemTime.getCurrentTime();
		
		return( (diff<0?"-":"") + DisplayFormatters.formatTime(Math.abs(diff)));
	
public com.aelitis.azureus.core.dht.DHTStorageBlock[]getDirectKeyBlocks()

		return( key_blocks_direct_cow );
	
public byte[][]getExistingDiversification(byte[] key, boolean put_operation, boolean exhaustive)

		//System.out.println( "DHT get existing diversification: put = " + put_operation  );
		
		HashWrapper	wrapper = new HashWrapper( key );
		
			// must always return a value - original if no diversification exists
		
		try{
			storage_mon.enter();
		
			byte[][]	res = followDivChain( wrapper, put_operation, exhaustive );
			
			if ( !Arrays.equals( res[0], key )){
				
				String	trace = "";
				
				for (int i=0;i<res.length;i++){
					trace += (i==0?"":",") + DHTLog.getString2( res[i] );
				}
				
				log.log( "SM: get div: " + DHTLog.getString2(key) + ", put = " + put_operation + ", exh = " + exhaustive + " -> " + trace );
			}
			
			return( res );
			
		}finally{
			
			storage_mon.exit();
		}
	
public com.aelitis.azureus.core.dht.DHTStorageBlockgetKeyBlockDetails(byte[] key)

		keyBlock	kb = (keyBlock)key_block_map_cow.get( key );
		
		if ( kb == null || !kb.isAdd()){
			
			return( null );
		}
		
		if ( !kb.getLogged()){
			
			kb.setLogged();
			
			log.log( "KB: Access to key '" + DHTLog.getFullString( kb.getKey()) + "' denied as it is blocked" );
		}
		
		return( kb );
	
public byte[]getKeyForKeyBlock(byte[] request)

		if ( request.length <= 8 ){
			
			return( new byte[0] );
		}
		
		byte[]	key = new byte[ request.length - 8 ];
		
		System.arraycopy( request, 8, key, 0, key.length );
		
		return( key );
	
protected java.lang.StringgetMostRecentAddress()

		byte[]	addr = (byte[])recent_addresses.get( "most_recent" );
		
		if ( addr == null ){
			
			return( null );
		}
		
		return( new String( addr ));
	
public intgetNextValueVersions(int num)

		try{
			version_mon.enter();

			Long	l_next = (Long)version_map.get( "next" );
			
			int	now = (int)(SystemTime.getCurrentTime()/1000);

			int	next;
			
			if ( l_next == null ){

				next = now;
				
			}else{
				
				next = l_next.intValue();
				
					// if "next" is in the future then we live with it to try and ensure increasing
					// values (system clock must have changed)
				
				if ( next < now ){
					
					next = now;
				}
			}
			
			version_map.put( "next", new Long( next+num ));
			
			writeVersionData();
			
			return( next );
			
		}finally{
			
			version_mon.exit();
		}
	
public byte[]getStorageForKey(java.lang.String key)

		try{
			storage_mon.enter();
			
			Map	map = readMapFromFile( "general" );
			
			return((byte[])map.get( key ));
			
		}finally{
			
			storage_mon.exit();
		}
	
protected com.aelitis.azureus.plugins.dht.impl.DHTPluginStorageManager$storageKeygetStorageKey(HashWrapper key)

		storageKey	res = (storageKey)local_storage_keys.get( key );
		
		if ( res == null ){
			
				// someout could be spamming us with crap, prevent things from getting
				// out of control
			
			if ( local_storage_keys.size() >= MAX_STORAGE_KEYS ){
				
				res = new storageKey( this, DHT.DT_SIZE, key ); 

				Debug.out( "DHTStorageManager: max key limit exceeded" );
				
				log.log( "SM: max storage key limit exceeded - " + DHTLog.getString2( key.getBytes()));
				
			}else{

				res = new storageKey( this, DHT.DT_NONE, key ); 
			
				local_storage_keys.put( key, res );
			}
		}
		
		return( res );
	
protected voidimportContacts(com.aelitis.azureus.core.dht.DHT dht)

		try{
			contact_mon.enter();
						
			File	target = new File( data_dir, "contacts.dat" );

			if ( !target.exists()){
				
				target	= new File( data_dir, "contacts.saving" );
			}

			if ( target.exists()){
				
				DataInputStream	dis =  new DataInputStream( new FileInputStream( target ));
				
				try{
					
					dht.importState( dis );
					
				}finally{
											
					dis.close();
				}
			}
		}catch( Throwable e ){
			
			Debug.printStackTrace( e );
			
		}finally{
			
			contact_mon.exit();
		}
	
public booleanisDiversified(byte[] key)

		HashWrapper	wrapper = new HashWrapper( key );
		
		try{
			storage_mon.enter();
		
			return( lookupDiversification( wrapper ) != null );
			
		}finally{
			
			storage_mon.exit();
		}
	
protected booleanisRecentAddress(java.lang.String address)

		try{
			address_mon.enter();

			if ( recent_addresses.containsKey( address )){
				
				return( true );
			}
					
			String	most_recent = getMostRecentAddress();
			
			return( most_recent != null && most_recent.equals( address ));
			
		}finally{
			
			address_mon.exit();
		}
	
public com.aelitis.azureus.core.dht.DHTStorageBlockkeyBlockRequest(com.aelitis.azureus.core.dht.transport.DHTTransportContact originating_contact, byte[] request, byte[] signature)

			// request is 4 bytes flags, 4 byte time, K byte key
			// flag: MSB 00 -> unblock, 01 ->block
		
		if ( request.length <= 8 ){
			
			return( null );
		}
				
		keyBlock	kb = 
			new keyBlock(request, signature, (int)(SystemTime.getCurrentTime()/1000), originating_contact != null );
		
		try{
			key_block_mon.enter();

			boolean	add_it	= false;
			
			try{
				keyBlock	old = (keyBlock)key_block_map_cow.get( kb.getKey());
								
				if ( old != null ){
				
						// never override a direct value with an indirect one as direct = first hand knowledge
						// whereas indirect is hearsay

					if ( old.isDirect() && !kb.isDirect()){
					
						return( null );
					}
					 
						// don't let older instructions override newer ones
					
					if ( old.getCreated() > kb.getCreated()){
						
						return( null );
					}
				}
				
				if ( kb.isAdd()){
					
					if ( old == null || !old.isAdd()){
						
						if ( !verifyKeyBlock( kb, originating_contact )){
							
							return( null );
						}
					
						add_it	= true;
					}
					
					return( kb );
					
				}else{
					
						// only direct operations can "remove" blocks
					
					if ( kb.isDirect() && ( old == null || old.isAdd())){
					
						if ( !verifyKeyBlock( kb, originating_contact )){
							
							return( null );
						}
						
						add_it	= true;
					}
					
					return( null );
				}
			}finally{
				
				if ( add_it ){
					
					ByteArrayHashMap new_map = key_block_map_cow.duplicate();
					
					new_map.put( kb.getKey(), kb );
					
						// seeing as we've received this from someone there's no point in replicating it
						// back to them later - mark them to prevent this
					
					if ( originating_contact != null ){
						
						kb.sentTo( originating_contact );
					}
					
					key_block_map_cow		= new_map;
					key_blocks_direct_cow	= buildKeyBlockDetails( key_block_map_cow );
					
					writeKeyBlocks();
				}
			}
		}finally{
			
			key_block_mon.exit();
		}
	
public com.aelitis.azureus.core.dht.DHTStorageKeykeyCreated(HashWrapper key, boolean local)

		//System.out.println( "DHT key created");
		
		try{
			storage_mon.enter();
		
			return(	getStorageKey( key ));
			
		}finally{
			
			storage_mon.exit();
		}
	
public voidkeyDeleted(com.aelitis.azureus.core.dht.DHTStorageKey key)

		//System.out.println( "DHT key deleted" );
		
		try{
			storage_mon.enter();
		
			deleteStorageKey((storageKey)key );
			
		}finally{
			
			storage_mon.exit();
		}
	
public voidkeyRead(com.aelitis.azureus.core.dht.DHTStorageKey key, com.aelitis.azureus.core.dht.transport.DHTTransportContact contact)

		//System.out.println( "DHT value read" );
		
		try{
			storage_mon.enter();
		
			((storageKey)key).read( contact );
			
		}finally{
			
			storage_mon.exit();
		}
	
protected voidlocalContactChanged(com.aelitis.azureus.core.dht.transport.DHTTransportContact contact)

		purgeDirectKeyBlocks();
	
protected com.aelitis.azureus.plugins.dht.impl.DHTPluginStorageManager$diversificationlookupDiversification(HashWrapper wrapper)

		diversification	div = (diversification)remote_diversifications.get(wrapper);
		
		if ( div != null ){
			
			if ( div.getExpiry() < SystemTime.getCurrentTime()){
				
				log.log( "SM: div: " + DHTLog.getString2( div.getKey().getBytes()) + " expired" );

				remote_diversifications.remove( wrapper );
				
				div = null;
			}
		}
		
		return( div );
	
protected voidpurgeDirectKeyBlocks()

		try{
			key_block_mon.enter();
		
			ByteArrayHashMap new_map = new ByteArrayHashMap();
			
			Iterator	it = key_block_map_cow.values().iterator();
			
			boolean	changed = false;
			
			while( it.hasNext()){
				
				keyBlock	kb = (keyBlock)it.next();
				
				if ( kb.isDirect()){
					
					changed	= true;
					
				}else{
					
					new_map.put( kb.getKey(), kb );
				}
			}
			
			if ( changed ){
				
				log.log( "KB: Purged direct entries on ID change" );
				
				key_block_map_cow		= new_map;
				key_blocks_direct_cow	= buildKeyBlockDetails( key_block_map_cow );
				
				writeKeyBlocks();
			}
		}finally{
			
			key_block_mon.exit();
		}
	
protected voidreadDiversifications()

		try{
			storage_mon.enter();
			
			Map	map = readMapFromFile( "diverse" );
	
			List	keys = (List)map.get("local");
			
			if ( keys != null ){
				
				long	now = SystemTime.getCurrentTime();
				
				for (int i=0;i<keys.size();i++){
					
					storageKey d = storageKey.deserialise(this, (Map)keys.get(i));
						
					long	time_left = d.getExpiry() - now;
					
					if ( time_left > 0 ){
					
						local_storage_keys.put( d.getKey(), d );
						
					}else{
						
						log.log( "SM: serialised sk: " + DHTLog.getString2( d.getKey().getBytes()) + " expired" );
					}
				}
			}
			List	divs = (List)map.get("remote");
			
			if ( divs != null ){
				
				long	now = SystemTime.getCurrentTime();
				
				for (int i=0;i<divs.size();i++){
					
					diversification d = diversification.deserialise( this, (Map)divs.get(i));
						
					long	time_left = d.getExpiry() - now;

					if ( time_left > 0 ){
					
						remote_diversifications.put( d.getKey(), d );
						
					}else{
						
						log.log( "SM: serialised div: " + DHTLog.getString2( d.getKey().getBytes()) + " expired" );
					}
				}
			}
			
		}finally{
			
			storage_mon.exit();
		}
	
protected voidreadKeyBlocks()

		try{
			key_block_mon.enter();
			
			Map	map = readMapFromFile( "block" );
	
			List	entries = (List)map.get( "entries" );
			
			int	now_secs = (int)(SystemTime.getCurrentTime()/1000);
			
			ByteArrayHashMap	new_map = new ByteArrayHashMap();
			
			if ( entries != null ){
			
				for (int i=0;i<entries.size();i++){
					
					try{
						Map	m = (Map)entries.get(i);
						
						byte[]	request = (byte[])m.get( "req" );
						byte[]	cert	= (byte[])m.get( "cert" );
						int		recv	= ((Long)m.get( "received" )).intValue();
						boolean	direct	= ((Long)m.get( "direct" )).longValue()==1;
						
						if ( recv > now_secs ){
							
							recv	= now_secs;
						}

						keyBlock	kb = new keyBlock( request, cert, recv, direct );

							// direct "add" values never timeout, however direct "removals" do, as do 
							// indirect values
						
						if ( ( direct && kb.isAdd()) || now_secs - recv < KEY_BLOCK_TIMEOUT_SECS ){
						
							if ( verifyKeyBlock( request, cert )){
								
								log.log( "KB: deserialised " + DHTLog.getString2( kb.getKey()) + ",add=" + kb.isAdd() + ",dir=" + kb.isDirect());
						
								new_map.put( kb.getKey(), kb );
							}
						}
						
					}catch( Throwable e ){
						
						Debug.printStackTrace(e);
					}
				}
			}
			
			key_block_map_cow		= new_map;
			key_blocks_direct_cow	= buildKeyBlockDetails( new_map );
			
		}finally{
			
			key_block_mon.exit();
		}
	
protected java.util.MapreadMapFromFile(java.lang.String file_prefix)

		try{
			File target = new File( data_dir, file_prefix + ".dat" );
			
			if ( !target.exists()){
				
				target	= new File( data_dir, file_prefix + ".saving" );
			}
			
			if ( target.exists()){
				
				BufferedInputStream	is = new BufferedInputStream( new FileInputStream( target ));
				
				try{
					return( BDecoder.decode( is ));
					
				}finally{
					
					is.close();
				}
			}
		}catch( Throwable e ){
			
			Debug.printStackTrace( e );	
		}		
		
		return( new HashMap());
	
protected voidreadRecentAddresses()

		try{
			address_mon.enter();
			
			recent_addresses = readMapFromFile( "addresses" );
	
		}finally{
			
			address_mon.exit();
		}
	
protected voidreadVersionData()

		try{
			version_mon.enter();
			
			version_map = readMapFromFile( "version" );
	
		}finally{
			
			version_mon.exit();
		}
	
protected voidrecordCurrentAddress(java.lang.String address)

		try{
			address_mon.enter();
			
			recent_addresses.put( address, new Long( SystemTime.getCurrentTime()));
		
			recent_addresses.put( "most_recent", address.getBytes());
			
			writeRecentAddresses();
			
		}finally{
			
			address_mon.exit();
		}
	
public voidserialiseStats(com.aelitis.azureus.plugins.dht.impl.DHTPluginStorageManager$storageKey key, java.io.DataOutputStream dos)

		dos.writeByte( (byte)0 );	// version
		dos.writeInt( key.getEntryCount());
		dos.writeInt( key.getSize());
		dos.writeInt( key.getReadsPerMinute());
		dos.writeByte( key.getDiversificationType());
	
public voidsetStorageForKey(java.lang.String key, byte[] data)

		try{
			storage_mon.enter();
			
			Map	map = readMapFromFile( "general" );
			
			map.put( key, data );
			
			writeMapToFile( map, "general" );
			
		}finally{
			
			storage_mon.exit();
		}
	
public voidvalueAdded(com.aelitis.azureus.core.dht.DHTStorageKey key, com.aelitis.azureus.core.dht.transport.DHTTransportValue value)

		// System.out.println( network + ": DHT value added: "  + DHTLog.getString2( ((storageKey)key).getKey().getBytes()) + " -> " + value.getString());
		
		try{
			storage_mon.enter();
		
			((storageKey)key).valueChanged( 1, value.getValue().length);
			
		}finally{
			
			storage_mon.exit();
		}
	
public voidvalueDeleted(com.aelitis.azureus.core.dht.DHTStorageKey key, com.aelitis.azureus.core.dht.transport.DHTTransportValue value)

		//System.out.println( "DHT value deleted" );
		
		try{
			storage_mon.enter();
		
			((storageKey)key).valueChanged( -1, -value.getValue().length);
			
		}finally{
			
			storage_mon.exit();
		}
	
public voidvalueUpdated(com.aelitis.azureus.core.dht.DHTStorageKey key, com.aelitis.azureus.core.dht.transport.DHTTransportValue old_value, com.aelitis.azureus.core.dht.transport.DHTTransportValue new_value)

		//System.out.println( "DHT value updated" );
		
		try{
			storage_mon.enter();
			
			((storageKey)key).valueChanged( 0, new_value.getValue().length - old_value.getValue().length);
			
		}finally{
			
			storage_mon.exit();
		}
	
protected booleanverifyKeyBlock(com.aelitis.azureus.plugins.dht.impl.DHTPluginStorageManager$keyBlock kb, com.aelitis.azureus.core.dht.transport.DHTTransportContact originator)

		byte[]	id = originator==null?new byte[20]:originator.getID();
		
		BloomFilter	filter = kb_verify_fail_bloom;
		
		long	now = SystemTime.getCurrentTime();
		
		if ( 	filter == null || 
				kb_verify_fail_bloom_create_time > now ||
				now - kb_verify_fail_bloom_create_time > 30*60*1000 ){
			
			kb_verify_fail_bloom_create_time	= now;
			
			filter = BloomFilterFactory.createAddOnly(4000);
			
			kb_verify_fail_bloom	= filter;
		}
		
		if ( filter.contains( id )){
			
			log.log( "KB: request verify denied" );
			
			return( false );
		}
		
		try{
			Signature	verifier = Signature.getInstance("MD5withRSA" );
			
			verifier.initVerify( key_block_public_key );
			
			verifier.update( kb.getRequest() );

			if ( !verifier.verify( kb.getCertificate())){
			
				log.log( "KB: request verify failed for " + DHTLog.getString2( kb.getKey()));

				filter.add( id );
				
				return( false );
			}
			
			log.log( "KB: request verify ok " + DHTLog.getString2( kb.getKey()) + ", add = " + kb.isAdd() + ", direct = " + kb.isDirect());

			return( true );
			
		}catch( Throwable e ){
			
			return( false );
		}
	
public static booleanverifyKeyBlock(byte[] request, byte[] signature)

		try{
			Signature	verifier = Signature.getInstance("MD5withRSA" );
			
			verifier.initVerify( key_block_public_key );
			
			verifier.update( request );

			if ( !verifier.verify( signature )){
			
				return( false );
			}
			
			return( true );
			
		}catch( Throwable e ){
			
			return( false );
		}
	
protected voidwriteDiversifications()

		try{
			storage_mon.enter();
	
			Map	map = new HashMap();
			
			List	keys = new ArrayList();
			
			map.put( "local", keys );
			
			Iterator	it = local_storage_keys.values().iterator();
			
			while( it.hasNext()){
			
				storageKey	key = (storageKey)it.next();
				
				if ( key.getDiversificationType() != DHT.DT_NONE ){
					
					keys.add(key.serialise());
				}
			}
			
			List	divs = new ArrayList();
			
			map.put( "remote", divs );
			
			it = remote_diversifications.values().iterator();
			
			while( it.hasNext()){
			
				divs.add(((diversification)it.next()).serialise());
			}
			
			writeMapToFile( map, "diverse" );
			
		}catch( Throwable e ){
			
			Debug.printStackTrace(e);
	
		}finally{
			
			storage_mon.exit();
		}
	
protected voidwriteKeyBlocks()

		try{
			key_block_mon.enter();
	
			Map	map = new HashMap();
			
			List	entries = new ArrayList();
			
			map.put( "entries", entries );
			
			List	kbs = key_block_map_cow.values();
			
			for (int i=0;i<kbs.size();i++){
								
				keyBlock	kb = (keyBlock)kbs.get(i);
									
				Map	m = new HashMap();
				
				m.put( "req", kb.getRequest());
				m.put( "cert", kb.getCertificate());
				m.put( "received", new Long(kb.getReceived()));
				m.put( "direct", new Long(kb.isDirect()?1:0));
				
				entries.add( m );
			}
			
			writeMapToFile( map, "block" );
			
		}catch( Throwable e ){
			
			Debug.printStackTrace(e);
	
		}finally{
			
			key_block_mon.exit();
		}
	
protected voidwriteMapToFile(java.util.Map map, java.lang.String file_prefix)

		try{
			File	saving = new File( data_dir, file_prefix + ".saving" );
			File	target = new File( data_dir, file_prefix + ".dat" );

			saving.delete();
			
			if ( map.size() == 0 ){
				
				target.delete();
				
			}else{
				
				FileOutputStream os = null;
				
				boolean	ok = false;
				
				try{
					byte[]	data = BEncoder.encode( map );
					
					os = new FileOutputStream( saving );
						
					os.write( data );
				
					os.flush();
				
					os.getFD().sync();
				
					os.close();
				
					ok	= true;
					
				}finally{
					
					if ( os != null ){
						
						os.close();
						
						if ( ok ){
							
							target.delete();
							
							saving.renameTo( target );
						}
					}
				}
			}
		}catch( Throwable e ){
			
			Debug.printStackTrace(e);
		}
	
protected voidwriteRecentAddresses()

		try{
			address_mon.enter();
			
				// remove any old crud
			
			Iterator	it = recent_addresses.keySet().iterator();
			
			while( it.hasNext()){
				
				String	key = (String)it.next();
				
				if ( !key.equals( "most_recent" )){
					
					Long	time = (Long)recent_addresses.get(key);
					
					if ( SystemTime.getCurrentTime() - time.longValue() > ADDRESS_EXPIRY ){
						
						it.remove();
					}
				}
			}
			
			writeMapToFile( recent_addresses, "addresses" );
			
		}catch( Throwable e ){
			
			Debug.printStackTrace(e);
	
		}finally{
			
			address_mon.exit();
		}
	
protected voidwriteVersionData()

		try{
			version_mon.enter();
			
			writeMapToFile( version_map, "version" );
	
		}finally{
			
			version_mon.exit();
		}