Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Track originating peer for block gossip #4270

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions ironfish/src/network/__fixtures__/blockFetcher.test.ts.fixture
Original file line number Diff line number Diff line change
Expand Up @@ -1127,5 +1127,33 @@
}
]
}
],
"BlockFetcher correctly sends the first peer to send us a block to telemetry": [
{
"header": {
"sequence": 2,
"previousBlockHash": "88B6FA8D745A4E53BDA001318E60B04EE2E4EE06A38095688D58049CB6F15ACA",
"noteCommitment": {
"type": "Buffer",
"data": "base64:Px07oo4o5eD8bwJc8+Lwj05tbzpOkTtMvOlZAXFNkQI="
},
"transactionCommitment": {
"type": "Buffer",
"data": "base64:qmz/w5yAd3chiU3gikRdoRBa0Q/hGDQqZk81NmGF/K4="
},
"target": "883423532389192164791648750371459257913741948437809479060803100646309888",
"randomness": "0",
"timestamp": 1693954844245,
"graffiti": "0000000000000000000000000000000000000000000000000000000000000000",
"noteSize": 4,
"work": "0"
},
"transactions": [
{
"type": "Buffer",
"data": "base64:AgAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGzKiP////8AAAAAS2N5wYtofXzfUcBVvTGuSHHt/dHM4ZpYJuhPnc02+UKoS94gAqXjnbpW3L13mtT2EZPM8UnWnb9eOZb9y2lCln6E5rbatrAQKACe1zOQFJ6MYNho6KhsPGEmHlUwYfFVupBY3pzW2w8T8mZ16OGBXm1SV07nb9iGAtmhIFD/z1IELEHvuuHhZ9YJ8nPaEo8kajk4dPrdO3FJn1Z4hcnohjwOilJ+HctHWStH9zHDZAmJOtm/CYr7SBz2xw0Avl3M4tj+MwQVB/G9tHdZzBS1aVlAMjJmkVGp0KMMhaS6xYwSwjmnTHw/sGPw8AtLF0Fn/wgjRowJl6awXFZB0BNZuIm3XQAHH3n87bz92jDKi6W39YjnqWjJEXiam7avPfZEsolL9bQLBz1I+BFdJ3ae3R8Y1LU1aIbd+V1C0aM15V6ZkdLJ3MP/rpuEog5I2HVDjbFTho20S46QKjvBIrRT0ILl0uJ5lyZDY82jZnA+ifWB9kesyUY7PCl0gi9nPCtzJH2P3/wU0cnXm/VTDVhUxSXLEwviYhnoJ92iKXD1CXNSA+g3LOnDxzwW9ytSH2B0oumrAHi4Mk3oa1oSObZ/QRL6YdNo7/4EoW1eqbquqwZ0x5I6SwNmMElyb24gRmlzaCBub3RlIGVuY3J5cHRpb24gbWluZXIga2V5MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAw+yOB3pVLpqIg/6GR2JgVgzoaWlnnW0+Tl+n2+PBSXcPnKBRo80iHhMeat2x9o4IvIoHtgPM7GdOk6MkbrQymAg=="
}
]
}
]
}
38 changes: 38 additions & 0 deletions ironfish/src/network/blockFetcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -464,4 +464,42 @@ describe('BlockFetcher', () => {

await peerNetwork.stop()
})

it('correctly sends the first peer to send us a block to telemetry', async () => {
const { peerNetwork, chain, node } = nodeTest

const peers = getConnectedPeersWithSpies(peerNetwork.peerManager, 50)

const newBlock = await useMinerBlockFixture(chain)

// Accept transactions into mempool so the node can fill the compact block
// without an additional request
for (const transaction of newBlock.transactions.slice(1)) {
node.memPool.acceptTransaction(transaction)
}

for (const { peer } of peers) {
await peerNetwork.peerManager.onMessage.emitAsync(...newHashMessageEvent(peer, newBlock))
}

jest.runOnlyPendingTimers()

const telemetrySpy = jest.spyOn(node.telemetry, 'submitNewBlockSeen')

const sentPeers = peers.filter(({ sendSpy }) => sendSpy.mock.calls.length > 0)
const call = sentPeers[0].sendSpy.mock.calls[0][0] as GetCompactBlockRequest

const message = new GetCompactBlockResponse(newBlock.toCompactBlock(), call.rpcId)
for (const { peer } of sentPeers) {
await peerNetwork.peerManager.onMessage.emitAsync(...peerMessage(peer, message))
}

expect(telemetrySpy).toHaveBeenCalledWith(
newBlock,
expect.any(Date),
peers[0].peer.state.identity,
)

await peerNetwork.stop()
})
})
23 changes: 22 additions & 1 deletion ironfish/src/network/blockFetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,22 @@ type BlockState =
action: 'COMPACT_BLOCK_REQUEST_SCHEDULED'
timeout: NodeJS.Timeout
sources: Set<Identity> // Set of peers that have sent us the hash or compact block
firstSeenBy: Identity
}
| {
action: 'COMPACT_BLOCK_REQUEST_IN_FLIGHT'
peer: Identity
timeout: NodeJS.Timeout
clearDisconnectHandler: () => void
sources: Set<Identity> // Set of peers that have sent us the hash or compact block
firstSeenBy: Identity
}
| {
action: 'PROCESSING_COMPACT_BLOCK'
peer: Identity
compactBlock: CompactBlock
sources: Set<Identity> // Set of peers that have sent us the hash or compact block
firstSeenBy: Identity
}
| {
action: 'TRANSACTION_REQUEST_IN_FLIGHT'
Expand All @@ -57,17 +60,20 @@ type BlockState =
timeout: NodeJS.Timeout
clearDisconnectHandler: () => void
sources: Set<Identity> // Set of peers that have sent us the hash or compact block
firstSeenBy: Identity
}
| {
action: 'FULL_BLOCK_REQUEST_IN_FLIGHT'
peer: Identity
timeout: NodeJS.Timeout
clearDisconnectHandler: () => void
sources: Set<Identity> // Set of peers that have sent us the hash or compact block
firstSeenBy: Identity
}
| {
action: 'PROCESSING_FULL_BLOCK'
block: Block
firstSeenBy: Identity | null
}

export class BlockFetcher {
Expand Down Expand Up @@ -110,6 +116,7 @@ export class BlockFetcher {
action: 'COMPACT_BLOCK_REQUEST_SCHEDULED',
timeout,
sources,
firstSeenBy: peer.state.identity,
})
}

Expand Down Expand Up @@ -173,6 +180,7 @@ export class BlockFetcher {
timeout,
clearDisconnectHandler,
sources: currentState.sources,
firstSeenBy: currentState.firstSeenBy,
})
}

Expand Down Expand Up @@ -215,10 +223,19 @@ export class BlockFetcher {
peer: peer.state.identity,
compactBlock,
sources: currentState ? currentState.sources : new Set<Identity>(),
firstSeenBy: currentState ? currentState.firstSeenBy : peer.state.identity,
})
return true
}

/**
* Return the first peer that notified us of this block
*/
firstSeenBy(hash: BlockHash): Identity | null {
const currentState = this.pending.get(hash)
return currentState ? currentState.firstSeenBy : null
}

requestBlockTransactions(
peer: Peer,
header: BlockHeader,
Expand Down Expand Up @@ -267,6 +284,7 @@ export class BlockFetcher {
timeout,
clearDisconnectHandler,
sources: currentState.sources,
firstSeenBy: currentState.firstSeenBy,
})

return
Expand Down Expand Up @@ -302,6 +320,7 @@ export class BlockFetcher {
this.pending.set(hash, {
action: 'PROCESSING_FULL_BLOCK',
block,
firstSeenBy: currentState.firstSeenBy,
})

return block
Expand Down Expand Up @@ -353,13 +372,14 @@ export class BlockFetcher {
timeout,
clearDisconnectHandler,
sources: currentState.sources,
firstSeenBy: currentState.firstSeenBy,
})
}

/**
* Called when a block has been assembled from a compact block
* but has not yet been validated and added to the chain. */
receivedFullBlock(block: Block): void {
receivedFullBlock(block: Block, peer: Peer): void {
const hash = block.header.hash

const currentState = this.pending.get(hash)
Expand All @@ -369,6 +389,7 @@ export class BlockFetcher {
this.pending.set(hash, {
action: 'PROCESSING_FULL_BLOCK',
block,
firstSeenBy: currentState?.firstSeenBy ?? peer.state.identity ?? null,
})
}
}
Expand Down
5 changes: 3 additions & 2 deletions ironfish/src/network/peerNetwork.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1199,12 +1199,13 @@ export class PeerNetwork {
}

// Mark that we've assembled a full block in the block fetcher
this.blockFetcher.receivedFullBlock(block)
this.blockFetcher.receivedFullBlock(block, peer)

this.broadcastBlock(block)

// log that we've validated the block enough to gossip it
this.telemetry.submitNewBlockSeen(block, new Date())
const firstPeer = this.blockFetcher.firstSeenBy(block.header.hash)
this.telemetry.submitNewBlockSeen(block, new Date(), firstPeer)

// verify the full block
const verified = await this.chain.verifier.verifyBlockAdd(block, prevHeader)
Expand Down
7 changes: 6 additions & 1 deletion ironfish/src/telemetry/telemetry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ export class Telemetry {
})
}

submitNewBlockSeen(block: Block, seenAt: Date): void {
submitNewBlockSeen(block: Block, seenAt: Date, peerId: Identity | null): void {
this.submit({
measurement: 'block_propagation',
timestamp: seenAt,
Expand All @@ -419,6 +419,11 @@ export class Telemetry {
type: 'integer',
value: block.header.timestamp.valueOf(),
},
{
name: 'firstSeenBy',
type: 'string',
value: peerId ?? '',
},
{
name: 'sequence',
type: 'integer',
Expand Down