Commit d813d34d authored by Cédric Moreau's avatar Cédric Moreau

[fix] #1379 + #1380: prevent expired documents to reach the mempool

parent 5d44b0ee
export enum DataErrors {
TRANSACTION_WINDOW_IS_PASSED,
MEMBERSHIP_WINDOW_IS_PASSED,
CERT_WINDOW_IS_PASSED,
INVALID_LEVELDB_IINDEX_DATA_WAS_KICKED,
INVALID_LEVELDB_IINDEX_DATA_TO_BE_KICKED,
IDENTITY_UID_NOT_FOUND,
......
......@@ -24,6 +24,7 @@ import {hashf} from "../common"
import {Indexer, SimpleTxInput} from "../indexer"
import {DBTx} from "../db/DBTx"
import {Tristamp} from "../common/Tristamp"
import {DataErrors} from "../common-libs/errors"
const constants = CommonConstants
......@@ -231,7 +232,7 @@ export const GLOBAL_RULES_HELPERS = {
tx.blockstampTime = basedBlock.medianTime;
const current = await dal.getCurrentBlockOrNull();
if (current && current.medianTime > basedBlock.medianTime + constants.TX_WINDOW) {
throw "Transaction has expired";
throw DataErrors[DataErrors.TRANSACTION_WINDOW_IS_PASSED];
}
}
}
......
......@@ -20,6 +20,7 @@ import {GLOBAL_RULES_HELPERS} from "../lib/rules/global_rules";
import {MembershipDTO} from "../lib/dto/MembershipDTO";
import {FIFOService} from "./FIFOService";
import {DBBlock} from "../lib/db/DBBlock"
import {DataErrors} from "../lib/common-libs/errors"
const constants = require('../lib/constants');
......@@ -75,6 +76,10 @@ export class MembershipService extends FIFOService {
}, this.conf.pair && this.conf.pair.pub))) {
throw constants.ERRORS.SANDBOX_FOR_MEMERSHIP_IS_FULL;
}
const expires_on = basedBlock ? basedBlock.medianTime + this.conf.msWindow : 0
if (current && expires_on < current.medianTime) {
throw DataErrors[DataErrors.MEMBERSHIP_WINDOW_IS_PASSED]
}
// Saves entry
await this.dal.savePendingMembership({
issuers: [entry.pubkey],
......@@ -90,7 +95,7 @@ export class MembershipService extends FIFOService {
idtyHash: entry.getIdtyHash(),
written: false,
written_number: null,
expires_on: basedBlock ? basedBlock.medianTime + this.conf.msWindow : 0,
expires_on,
signature: entry.signature,
expired: false,
block_number: entry.number
......
// Source file from duniter: Crypto-currency software to manage libre currency such as Ğ1
// Copyright (C) 2018 Cedric Moreau <cem.moreau@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
import {writeBasicTestWithConfAnd2Users} from "../tools/test-framework"
import {assertThrows} from "../../unit-tools"
import {CommonConstants} from "../../../app/lib/common-libs/constants"
import {CertificationDTO} from "../../../app/lib/dto/CertificationDTO"
describe('Expired certifications', () => writeBasicTestWithConfAnd2Users({
sigReplay: 3,
sigPeriod: 0,
sigValidity: 10,
sigWindow: 2, // <--- this is the important parameter for this test!
}, (test) => {
before(() => {
CommonConstants.BLOCK_NEW_GENERATED_VERSION = 11
})
const now = 1500000000
let certToResend: CertificationDTO
test('should be able to init with 2 blocks', async (s1, cat, tac) => {
await cat.createIdentity()
await tac.createIdentity()
await cat.cert(tac)
await tac.cert(cat)
await cat.join()
await tac.join()
await s1.commit({ time: now, version: 10 })
await s1.commit({ time: now })
})
test('should accept toc, a new member at t+2', async (s1, cat, tac, toc) => {
await s1.commit({ time: now + 2 })
await s1.commit({ time: now + 2 })
await toc.createIdentity()
certToResend = await cat.makeCert(toc)
await cat.sendCert(certToResend)
await toc.join()
await s1.commit({ time: now + 2 })
})
test('should **NOT** be able to send again a written cert, even if replay time is passed', async (s1, cat, tac) => {
await s1.commit({ time: now + 6 })
await s1.commit({ time: now + 6 })
await assertThrows(cat.sendCert(certToResend), '{\n "ucode": 1002,\n "message": "CERT_WINDOW_IS_PASSED"\n}')
})
after(() => {
CommonConstants.BLOCK_NEW_GENERATED_VERSION = 10
})
}))
// Source file from duniter: Crypto-currency software to manage libre currency such as Ğ1
// Copyright (C) 2018 Cedric Moreau <cem.moreau@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
import {writeBasicTestWithConfAnd2Users} from "../tools/test-framework"
import {assertThrows} from "../../unit-tools"
import {CommonConstants} from "../../../app/lib/common-libs/constants"
describe('Expired identities', () => writeBasicTestWithConfAnd2Users({
sigReplay: 3,
sigPeriod: 0,
sigValidity: 10,
idtyWindow: 2, // <--- this is the important parameter for this test!
}, (test) => {
before(() => {
CommonConstants.BLOCK_NEW_GENERATED_VERSION = 11
})
const now = 1500000000
test('should be able to init with 2 blocks', async (s1, cat, tac) => {
await cat.createIdentity()
await tac.createIdentity()
await cat.cert(tac)
await tac.cert(cat)
await cat.join()
await tac.join()
await s1.commit({ time: now, version: 10 })
await s1.commit({ time: now })
})
test('should **NOT** accept an expired identity', async (s1, cat, tac, toc) => {
const idty = await toc.makeIdentity()
await s1.commit({ time: now + 6 })
await s1.commit({ time: now + 6 })
await assertThrows(toc.submitIdentity(idty), '{\n "ucode": 1002,\n "message": "Identity is too old and cannot be written"\n}')
})
after(() => {
CommonConstants.BLOCK_NEW_GENERATED_VERSION = 10
})
}))
// Source file from duniter: Crypto-currency software to manage libre currency such as Ğ1
// Copyright (C) 2018 Cedric Moreau <cem.moreau@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
import {writeBasicTestWithConfAnd2Users} from "../tools/test-framework"
import {assertThrows} from "../../unit-tools"
import {CommonConstants} from "../../../app/lib/common-libs/constants"
describe('Expired membership', () => writeBasicTestWithConfAnd2Users({
sigReplay: 3,
sigPeriod: 0,
sigValidity: 10,
msWindow: 2, // <--- this is the important parameter for this test!
}, (test) => {
before(() => {
CommonConstants.BLOCK_NEW_GENERATED_VERSION = 11
})
const now = 1500000000
test('should be able to init with 2 blocks', async (s1, cat, tac) => {
await cat.createIdentity()
await tac.createIdentity()
await cat.cert(tac)
await tac.cert(cat)
await cat.join()
await tac.join()
await s1.commit({ time: now, version: 10 })
await s1.commit({ time: now })
})
test('should accept toc, a new member at t+2', async (s1, cat, tac, toc) => {
await s1.commit({ time: now + 2 })
await s1.commit({ time: now + 2 })
await toc.createIdentity()
await cat.cert(toc)
await toc.join()
await s1.commit({ time: now + 2 })
})
test('should **NOT** be able to send an expired membership', async (s1, cat, tac, toc) => {
await s1.commit({ time: now + 3 })
await s1.commit({ time: now + 3 }) // <---- Time pass, so that the new membership becomes legit
const expiredMsAtTplus6 = await toc.makeMembership('IN')
await s1.commit({ time: now + 6 })
await s1.commit({ time: now + 6 }) // <---- But we want to make it expire
await assertThrows(toc.sendMembership(expiredMsAtTplus6), '{\n "ucode": 1002,\n "message": "MEMBERSHIP_WINDOW_IS_PASSED"\n}')
})
after(() => {
CommonConstants.BLOCK_NEW_GENERATED_VERSION = 10
})
}))
// Source file from duniter: Crypto-currency software to manage libre currency such as Ğ1
// Copyright (C) 2018 Cedric Moreau <cem.moreau@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
import {writeBasicTestWithConfAnd2Users} from "../tools/test-framework"
import {assertThrows} from "../../unit-tools"
import {CommonConstants} from "../../../app/lib/common-libs/constants"
describe('Expired transactions', () => writeBasicTestWithConfAnd2Users({
udTime0: 1500000000 - 1,
}, (test) => {
let oldTxWindowValue: number
before(() => {
CommonConstants.BLOCK_NEW_GENERATED_VERSION = 11
oldTxWindowValue = CommonConstants.TX_WINDOW
CommonConstants.TX_WINDOW = 2 // We need a low value to pass time bounds rules
})
const now = 1500000000
test('should be able to init with 2 blocks', async (s1, cat, tac) => {
await cat.createIdentity()
await tac.createIdentity()
await cat.cert(tac)
await tac.cert(cat)
await cat.join()
await tac.join()
await s1.commit({ time: now, version: 10 })
await s1.commit({ time: now })
})
test('should **NOT** accept an expired transaction', async (s1, cat, tac, toc) => {
const rawTX = await cat.prepareITX(100, tac)
await s1.commit({ time: now + CommonConstants.TX_WINDOW + 1 })
await s1.commit({ time: now + CommonConstants.TX_WINDOW + 1 }) // <---- This is the important change! Make the TX expire
await assertThrows(toc.sendTX(rawTX), '{\n "ucode": 1002,\n "message": "TRANSACTION_WINDOW_IS_PASSED"\n}')
})
after(() => {
CommonConstants.BLOCK_NEW_GENERATED_VERSION = 10
CommonConstants.TX_WINDOW = oldTxWindowValue
})
}))
......@@ -69,6 +69,11 @@ export class TestUser {
}
public async createIdentity(useRoot?:boolean|null, fromServer?:any) {
const idty = await this.makeIdentity(useRoot)
await this.submitIdentity(idty, fromServer);
}
public async makeIdentity(useRoot?:boolean|null) {
if (!this.pub) {
this.init(() => {})
}
......@@ -80,11 +85,10 @@ export class TestUser {
issuer: this.pub,
currency: this.node.server.conf.currency
}).getRawUnSigned()
this.createdIdentity += KeyGen(this.pub, this.sec).signSync(this.createdIdentity) + '\n'
await this.submitIdentity(this.createdIdentity, fromServer);
return this.createdIdentity += KeyGen(this.pub, this.sec).signSync(this.createdIdentity) + '\n'
}
public submitIdentity(raw:string, fromServer:any) {
public submitIdentity(raw:string, fromServer?: TestingServer) {
return this.doPost('/wot/add', {
"identity": raw
}, fromServer)
......@@ -118,17 +122,21 @@ export class TestUser {
public async cert(user:TestUser, fromServer?:TestingServer, toServer?:TestingServer) {
const cert = await this.makeCert(user, fromServer)
await this.doPost('/wot/certify', {
await this.sendCert(cert, toServer)
}
public async sendCert(cert: CertificationDTO, toServer?: TestingServer) {
return this.doPost('/wot/certify', {
"cert": cert.getRawSigned()
}, toServer);
}, toServer)
}
public async join() {
return await this.sendMembership("IN")
return await this.publishMembership("IN")
}
public async leave() {
return await this.sendMembership("OUT")
return await this.publishMembership("OUT")
}
public async makeRevocation(givenLookupIdty?:HttpLookup, overrideProps?:any) {
......@@ -181,11 +189,15 @@ export class TestUser {
return MembershipDTO.fromJSONObject(join)
}
public async sendMembership(type:string) {
public async publishMembership(type:string) {
const ms = await this.makeMembership(type);
await this.post('/blockchain/membership', {
await this.sendMembership(ms)
}
public async sendMembership(ms: MembershipDTO, toServer?: TestingServer) {
return this.doPost('/blockchain/membership', {
"membership": ms.getRawSigned()
})
}, toServer)
}
public async sendMoney(amount:number, recipient:TestUser, comment?:string) {
......@@ -194,8 +206,7 @@ export class TestUser {
}
public async sendTX(rawTX:string) {
let http = await this.getContacter()
return http.processTransaction(rawTX)
return this.doPost('/tx/process', { transaction: rawTX })
}
public async prepareUTX(previousTX:string, unlocks:string[], outputs:TestOutput[], opts:any) {
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment