if(recordsToSanitate.containsKey(record.getId())) { recordsToSanitate.remove(record.getId()); Set<HashId> idsToRemove = new HashSet<>(); for (StateRecord r : recordsToSanitate.values()) { if (r.getLockedByRecordId() == record.getRecordId()) { try { itemLock.synchronize(r.getId(), lock -> { if (record.getState() == ItemState.APPROVED) { if (r.getState() == ItemState.LOCKED) { r.setState(ItemState.REVOKED); r.save(); synchronized (cache) { cache.update(r.getId(), new ItemResult(r)); idsToRemove.add(r.getId()); } else if (r.getState() == ItemState.LOCKED_FOR_CREATION) { r.setState(ItemState.APPROVED); r.save(); synchronized (cache) { cache.update(r.getId(), new ItemResult(r)); idsToRemove.add(r.getId()); } else if (record.getState() == ItemState.DECLINED) { if (r.getState() == ItemState.LOCKED) { r.setState(ItemState.APPROVED);
@Override public void save(StateRecord stateRecord) { if (stateRecord.getLedger() == null) { stateRecord.setLedger(this); } else if (stateRecord.getLedger() != this) throw new IllegalStateException("can't save with adifferent ledger (make a copy!)"); if (stateRecord.getRecordId() == 0) { try ( PreparedStatement statement = "insert into ledger(hash,state,created_at, expires_at, locked_by_id) values(?,?,?,?,?);") ) { statement.setBytes(1, stateRecord.getId().getDigest()); statement.setInt(2, stateRecord.getState().ordinal()); statement.setLong(3, StateRecord.unixTime(stateRecord.getCreatedAt())); statement.setLong(4, StateRecord.unixTime(stateRecord.getExpiresAt())); statement.setLong(5, stateRecord.getLockedByRecordId()); statement.executeUpdate(); try (ResultSet keys = statement.getGeneratedKeys()) { throw new RuntimeException("generated keys are not supported"); long id = keys.getLong(1); stateRecord.setRecordId(id); } else { db.update("update ledger set state=?, expires_at=?, locked_by_id=? where id=?", stateRecord.getState().ordinal(), StateRecord.unixTime(stateRecord.getExpiresAt()), stateRecord.getLockedByRecordId(),
@Override public String toString() { return "State<"+getId()+"/"+getRecordId()+":"+getState()+":"+getCreatedAt()+"/"+getExpiresAt()+">"; } }
/** * Initialize from a record and posession flag * * @param record record to get data from * @param haveCopy true if the node has a copy of the {@link Approvable} item (e.g. one can try go call {@link * com.icodici.universa.node2.network.Network#getItem(HashId, NodeInfo, Duration)} on it */ public ItemResult(StateRecord record, boolean haveCopy) { // if( record == null ) { // throw new IllegalStateException("record can not be null"); // } state = record.getState(); this.haveCopy = haveCopy; createdAt = record.getCreatedAt(); expiresAt = record.getExpiresAt(); }
/** * Unlock the record if it was in a locked state, does nothing otherwise. * * @return self {@link StateRecord} */ public StateRecord unlock() { switch (state) { case LOCKED: setState(ItemState.APPROVED); setLockedByRecordId(0); break; case LOCKED_FOR_CREATION: destroy(); break; default: break; } return this; }
originRecord.setExpiresAt(origin.getExpiresAt()); originRecord.setCreatedAt(origin.getCreatedAt()); newRevisionRecord.setExpiresAt(newRevision.getExpiresAt()); newRevisionRecord.setCreatedAt(newRevision.getCreatedAt()); newContractRecord.setExpiresAt(newContract.getExpiresAt()); newContractRecord.setCreatedAt(newContract.getCreatedAt()); originRecord.setState(ItemState.REVOKED); newContractRecord.setState(ItemState.APPROVED); newRevisionRecord.setState(ItemState.APPROVED); } else { originRecord.setState(ItemState.APPROVED); newContractRecord.setState(ItemState.UNDEFINED); newRevisionRecord.setState(ItemState.DECLINED); originRecord.setState(ItemState.LOCKED); originRecord.setLockedByRecordId(newRevisionRecord.getRecordId()); newContractRecord.setState(ItemState.LOCKED_FOR_CREATION); newContractRecord.setLockedByRecordId(newRevisionRecord.getRecordId()); newRevisionRecord.setState(finalI < N/2 ? ItemState.PENDING_POSITIVE : ItemState.PENDING_NEGATIVE); originRecord.save(); ledger.putItem(originRecord,origin, Instant.now().plusSeconds(3600*24)); newRevisionRecord.save(); ledger.putItem(newRevisionRecord,newRevision, Instant.now().plusSeconds(3600*24)); if(newContractRecord.getState() == ItemState.UNDEFINED) { newContractRecord.destroy();
@Test public void checkLockOwner() throws Exception { ledger.enableCache(true); StateRecord existing = ledger.findOrCreate(HashId.createRandom()); existing.approve(); StateRecord r = ledger.findOrCreate(HashId.createRandom()); StateRecord r1 = r.lockToRevoke(existing.getId()); existing.reload(); r.reload(); assertSameRecords(existing, r1); assertEquals(ItemState.LOCKED, existing.getState()); assertEquals(r.getRecordId(), existing.getLockedByRecordId()); StateRecord currentOwner = ledger.getLockOwnerOf(existing); System.out.println("existing: " + existing.getId()); System.out.println("locker: " + r.getId()); System.out.println("locked: " + r1.getId()); System.out.println("currentOwner: " + currentOwner.getId()); assertSameRecords(r, currentOwner); }
synchronized (recordsToSanitate) { for (StateRecord r : recordsToSanitate.values()) { if (r.getState() != ItemState.LOCKED && r.getState() != ItemState.LOCKED_FOR_CREATION && !sanitatingIds.contains(r.getId())) { sanitateRecord(r); sanitatingIds.add(r.getId()); if (sanitatingIds.size() == MAX_SANITATING_RECORDS) { break; r.setState(ItemState.PENDING); try { itemLock.synchronize(r.getId(), lock -> { r.save(); synchronized (cache) { cache.update(r.getId(), new ItemResult(r));
@Test public void createOutputLockRecord() throws Exception { ledger.enableCache(true); StateRecord owner = ledger.findOrCreate(HashId.createRandom()); StateRecord other = ledger.findOrCreate(HashId.createRandom()); HashId id = HashId.createRandom(); StateRecord r1 = owner.createOutputLockRecord(id); r1.reload(); assertEquals(id, r1.getId()); assertEquals(ItemState.LOCKED_FOR_CREATION, r1.getState()); assertEquals(owner.getRecordId(), r1.getLockedByRecordId()); StateRecord r2 = owner.createOutputLockRecord(id); assertSame(r2, r1); assertNull(owner.createOutputLockRecord(other.getId())); // And hacked low level operation must fail too assertNull(ledger.createOutputLockRecord(owner.getRecordId(), other.getId())); }
@Test public void revoke() throws Exception { StateRecord r1 = ledger.findOrCreate(HashId.createRandom()); assertFalse(r1.isApproved()); assertTrue(r1.isPending()); assertFalse(r1.isArchived()); r1.approve(); r1.reload(); assertTrue(r1.isApproved()); assertFalse(r1.isPending()); assertFalse(r1.isArchived()); r1.setState(ItemState.LOCKED); r1.revoke(); assertFalse(r1.isPending()); assertFalse(r1.isApproved()); assertTrue(r1.isArchived()); }
checkLedgerExists(); if (state != ItemState.PENDING) throw new IllegalStateException("only pending records are allowed to lock others"); return null; switch (lockedRecord.getState()) { case LOCKED: if( !checkLockedRecord(lockedRecord) ) return null; break; lockedRecord.setLockedByRecordId(recordId); lockedRecord.setState(ItemState.LOCKED); lockedRecord.save();
protected void assertSameRecords(StateRecord r, StateRecord r1) { assertEquals(r.getId(), r1.getId()); assertEquals(r.getState(), r1.getState()); assertAlmostSame(r.getCreatedAt(), r1.getCreatedAt()); assertEquals(r.getRecordId(), r1.getRecordId()); assertEquals(r.getLockedByRecordId(), r1.getLockedByRecordId()); }
@Test public void recordExpiration() throws Exception { // todo: expired can't be get - it should be dropped by the database HashId hashId = HashId.createRandom(); StateRecord r = ledger.findOrCreate(hashId); assertNotNull(r.getExpiresAt()); assert(r.getExpiresAt().isAfter(ZonedDateTime.now())); long recordId = r.getRecordId(); ZonedDateTime inFuture = ZonedDateTime.now().plusHours(2); r.setExpiresAt(inFuture); StateRecord r1 = ledger.getRecord(hashId); assertNotEquals(r1.getExpiresAt(), inFuture); r.save(); r1 = ledger.getRecord(hashId); assertAlmostSame(r.getExpiresAt(), r1.getExpiresAt()); r.setExpiresAt(ZonedDateTime.now().minusHours(1)); r.save(); r1 = ledger.getRecord(hashId); assertNull(r1); }
itemLock.synchronize(hashId, lock -> { StateRecord newRecord = ledger.findOrCreate(hashId); newRecord.setState(committingState) .setCreatedAt(createdAt) .setExpiresAt(expiresAt) .save(); this.record = newRecord; synchronized (cache) { cache.update(newRecord.getId(), new ItemResult(newRecord));
r0.setExpiresAt(r0.getExpiresAt().minusHours(1)); r0.save(); r1.setCreatedAt(r1.getCreatedAt().plusHours(1)); r1.save();
@Test public void checkNegativeBytesInId() throws Exception { HashId id = HashId.withDigest(Do.randomNegativeBytes(64)); StateRecord r1 = ledger.findOrCreate(id); r1.setState(ItemState.DECLINED); r1.save(); StateRecord r2 = ledger.getRecord(id); assertNotNull(r2); assertNotSame(r1, r2); assertEquals(r1.getState(), r2.getState()); ledger.enableCache(true); StateRecord r3 = ledger.getRecord(id); StateRecord r4 = ledger.getRecord(id); assertEquals(r3.toString(), r4.toString()); // why? assertSame(r3, r4); }
for (StateRecord r : lockedToRevoke) { try { itemLock.synchronize(r.getId(), lock -> { r.unlock().save(); synchronized (cache) { ItemResult cr = cache.getResult(r.getId()); ItemResult rr = new ItemResult(r); if(cr != null) { rr.extraDataBinder = cr.extraDataBinder; cache.update(r.getId(), rr); itemLock.synchronize(r.getId(), lock -> { r.unlock().save(); synchronized (cache) { ItemResult cr = cache.getResult(r.getId()); ItemResult rr = new ItemResult(r); if(cr != null) { rr.extraDataBinder = cr.extraDataBinder; cache.update(r.getId(), rr); .plus(newState == ItemState.REVOKED ? config.getRevokedItemExpiration() : config.getDeclinedItemExpiration()); record.setExpiresAt(expiration); try { synchronized (mutex) { if (newState != ItemState.UNDEFINED) { record.save(); // TODO: current implementation will cause an inner dbPool.db() invocation
protected synchronized void destroyCurrentFromAllNodesIfExists(Contract finalC) { for (Node nodeS : nodesMap.values()) { StateRecord r = nodeS.getLedger().getRecord(finalC.getId()); if (r != null) { r.destroy(); } } }
private void itemSanitationFailed(StateRecord record) { if(recordsToSanitate.containsKey(record.getId())) { record.setState(ItemState.PENDING); try { itemLock.synchronize(record.getId(), lock -> { record.save(); synchronized (cache) { cache.update(record.getId(), new ItemResult(record)); record.setState(ItemState.UNDEFINED); try { itemLock.synchronize(record.getId(), lock -> { record.destroy(); return null; });