Skip to content

Commit

Permalink
Merge remote-tracking branch 'ArkEcosystem/core/2.6' into add-nonce-t…
Browse files Browse the repository at this point in the history
…o-db

* ArkEcosystem/core/2.6:
  refactor(core-state): expose current block for transaction handlers (#2856)
  • Loading branch information
vasild committed Aug 1, 2019
2 parents 109e402 + f68c724 commit 410a566
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 54 deletions.
142 changes: 88 additions & 54 deletions __tests__/unit/core-state/wallets/wallet-manager.test.ts
Expand Up @@ -49,7 +49,7 @@ describe("Wallet Manager", () => {
});
});

describe("applyBlock", () => {
describe("block processing", () => {
let delegateMock;
let block2: Interfaces.IBlock;

Expand All @@ -64,7 +64,13 @@ describe("Wallet Manager", () => {
}

beforeEach(() => {
delegateMock = { applyBlock: jest.fn(), publicKey: delegatePublicKey, isDelegate: () => false };
delegateMock = {
applyBlock: jest.fn(),
revertBlock: jest.fn(),
publicKey: delegatePublicKey,
isDelegate: () => false,
};

// @ts-ignore
jest.spyOn(walletManager, "findByPublicKey").mockReturnValue(delegateMock);
jest.spyOn(walletManager, "applyTransaction").mockImplementation();
Expand All @@ -81,78 +87,106 @@ describe("Wallet Manager", () => {
walletManager.reindex(delegateMock);
});

it("should apply sequentially the transactions of the block", async () => {
await walletManager.applyBlock(block2);
describe("applyBlock", () => {
it("should apply sequentially the transactions of the block", async () => {
await walletManager.applyBlock(block2);

for (let i = 0; i < block2.transactions.length; i++) {
expect(walletManager.applyTransaction).toHaveBeenNthCalledWith(i + 1, block2.transactions[i]);
}
});
for (let i = 0; i < block2.transactions.length; i++) {
expect(walletManager.applyTransaction).toHaveBeenNthCalledWith(i + 1, block2.transactions[i]);
}
});

it("should apply the block data to the delegate", async () => {
await walletManager.applyBlock(block);
it("should apply the block data to the delegate", async () => {
await walletManager.applyBlock(block);

expect(delegateMock.applyBlock).toHaveBeenCalledWith(block.data);
});
expect(delegateMock.applyBlock).toHaveBeenCalledWith(block.data);
});

describe("when 1 transaction fails while applying it", () => {
it("should revert sequentially (from last to first) all the transactions of the block", async () => {
// @ts-ignore
jest.spyOn(walletManager, "applyTransaction").mockImplementation(tx => {
if (tx === block2.transactions[2]) {
throw new Error("Fake error");
describe("when 1 transaction fails while applying it", () => {
it("should revert sequentially (from last to first) all the transactions of the block", async () => {
// @ts-ignore
jest.spyOn(walletManager, "applyTransaction").mockImplementation(tx => {
if (tx === block2.transactions[2]) {
throw new Error("Fake error");
}
});

expect(block2.transactions.length).toBe(3);

try {
await walletManager.applyBlock(block2);

expect(undefined).toBe("this should fail if no error is thrown");
} catch (error) {
expect(walletManager.revertTransaction).toHaveBeenCalledTimes(2);
// tslint:disable-next-line: ban
block2.transactions.slice(0, 1).forEach((transaction, i, total) => {
expect(walletManager.revertTransaction).toHaveBeenNthCalledWith(
total.length + 1 - i,
block2.transactions[i],
);
});
}
});

expect(block2.transactions.length).toBe(3);
it("throws the Error", async () => {
walletManager.applyTransaction = jest.fn(tx => {
throw new Error("Fake error");
});

try {
await walletManager.applyBlock(block2);
try {
await walletManager.applyBlock(block2);

expect(undefined).toBe("this should fail if no error is thrown");
} catch (error) {
expect(walletManager.revertTransaction).toHaveBeenCalledTimes(2);
// tslint:disable-next-line: ban
block2.transactions.slice(0, 1).forEach((transaction, i, total) => {
expect(walletManager.revertTransaction).toHaveBeenNthCalledWith(
total.length + 1 - i,
block2.transactions[i],
);
});
}
expect(undefined).toBe("this should fail if no error is thrown");
} catch (error) {
expect(error).toBeInstanceOf(Error);
expect(error.message).toBe("Fake error");
}
});
});

it("throws the Error", async () => {
walletManager.applyTransaction = jest.fn(tx => {
throw new Error("Fake error");
});
it("should return the current block", async () => {
expect(walletManager.getCurrentBlock()).toBeUndefined();

try {
await walletManager.applyBlock(block2);
const applyTransaction = jest.spyOn(walletManager, "applyTransaction").mockImplementationOnce(() => {
expect(walletManager.getCurrentBlock()).toBe(block2);
});

expect(undefined).toBe("this should fail if no error is thrown");
} catch (error) {
expect(error).toBeInstanceOf(Error);
expect(error.message).toBe("Fake error");
}
await walletManager.applyBlock(block2);
expect(applyTransaction).toHaveBeenCalled();
expect(walletManager.getCurrentBlock()).toBeUndefined();
});
});

describe.skip("the delegate of the block is not indexed", () => {
describe("not genesis block", () => {
it("throw an Error", () => {});
});
describe.skip("the delegate of the block is not indexed", () => {
describe("not genesis block", () => {
it("throw an Error", () => {});
});

describe("genesis block", () => {
it("generates a new wallet", () => {});
describe("genesis block", () => {
it("generates a new wallet", () => {});
});
});
});
});

describe.skip("revertBlock", () => {
it("should revert all transactions of the block", () => {});
describe("revertBlock", () => {
it.todo("should revert all transactions of the block");

it.todo("should revert the block of the delegate");

it("should return the current block", async () => {
expect(walletManager.getCurrentBlock()).toBeUndefined();

it("should revert the block of the delegate", () => {});
const revertTransaction = jest
.spyOn(walletManager, "revertTransaction")
.mockImplementationOnce(async () => {
expect(walletManager.getCurrentBlock()).toBe(block2);
});

await walletManager.revertBlock(block2);
expect(revertTransaction).toHaveBeenCalled();
expect(walletManager.getCurrentBlock()).toBeUndefined();
});
});
});

describe("applyTransaction", () => {
Expand Down
2 changes: 2 additions & 0 deletions packages/core-interfaces/src/core-state/wallets.ts
Expand Up @@ -95,6 +95,8 @@ export interface IWalletManager {

reindex(wallet: IWallet): void;

getCurrentBlock(): Readonly<Interfaces.IBlock>;

clone(): IWalletManager;

loadActiveDelegateList(roundInfo: IRoundInfo): IWallet[];
Expand Down
11 changes: 11 additions & 0 deletions packages/core-state/src/wallets/wallet-manager.ts
Expand Up @@ -13,6 +13,7 @@ export class WalletManager implements State.IWalletManager {
public logger: Logger.ILogger = app.resolvePlugin<Logger.ILogger>("logger");

private readonly indexes: Record<string, State.IWalletIndex> = {};
private currentBlock: Interfaces.IBlock;

constructor() {
this.reset();
Expand Down Expand Up @@ -174,6 +175,10 @@ export class WalletManager implements State.IWalletManager {
}
}

public getCurrentBlock(): Readonly<Interfaces.IBlock> {
return this.currentBlock;
}

public clone(): WalletManager {
return new TempWalletManager(this);
}
Expand Down Expand Up @@ -206,6 +211,7 @@ export class WalletManager implements State.IWalletManager {
}

public async applyBlock(block: Interfaces.IBlock): Promise<void> {
this.currentBlock = block;
const generatorPublicKey: string = block.data.generatorPublicKey;

let delegate: State.IWallet;
Expand Down Expand Up @@ -252,13 +258,16 @@ export class WalletManager implements State.IWalletManager {
}

throw error;
} finally {
this.currentBlock = undefined;
}
}

public async revertBlock(block: Interfaces.IBlock): Promise<void> {
if (!this.has(block.data.generatorPublicKey)) {
app.forceExit(`Failed to lookup generator '${block.data.generatorPublicKey}' of block '${block.data.id}'.`);
}
this.currentBlock = block;

const delegate: State.IWallet = this.findByPublicKey(block.data.generatorPublicKey);
const revertedTransactions: Interfaces.ITransaction[] = [];
Expand Down Expand Up @@ -290,6 +299,8 @@ export class WalletManager implements State.IWalletManager {
}

throw error;
} finally {
this.currentBlock = undefined;
}
}

Expand Down

0 comments on commit 410a566

Please sign in to comment.