Skip to content

checks at FullNode side - #431

Closed
KryptSai wants to merge 38 commits into
developmentfrom
sai/checks-at-fullnode
Closed

KryptSai wants to merge 38 commits into
developmentfrom
sai/checks-at-fullnode

Conversation

@KryptSai

@KryptSai KryptSai commented Dec 31, 2025

Copy link
Copy Markdown
Contributor

In this PR, we are adding the following validations at fullnode side while syncing them from other nodes.

  1. While adding an incoming block to fullnode's levelDB it checks whether, previous block_ID of the incoming block matches with block_ID of the existing latest block or not.
    ex: let's say FullNode is having 10blocks, 11th block it is syncing from other node, then 11th block's previous block ID should be same as existing 10th block's ID.
  2. If the incoming block is a transfer block, FullNode checks whether the sender of the incoming block matches with the owner of the latest existing block or not.
    ex: let's say FullNode is having 10blocks, 11th block it is syncing from other node, then 11th block's senderDID, should be same as existing 10th block's OwnerDID.

Fullnode handles syncing token in the following 3 ways:
Lets say FullNode has 7 blocks of a particular token t1.

  1. case:If the publisher node publishes any blocks with block numbers greater than 7th(lets say from 8th to 10th)
    In this case, FullNode need to sync the incoming block. So while adding each block to Fullnode's levelDB it checks the above conditions. It will add only the above checks pass.
  2. case: If the publisher node publishes a block with block number 7.
    In this case, Fullnode checks whether is it the same block which is coming or is it the different block. If it is the different block, then it will add it into a double spend tokens table with DID1 &DID2 having the same block.
  3. case: If the publisher node publishes a block with block number less than 7(lets say 5).
    In this case, Fullnode doesn't need to sync. So it checks whether the incoming block(5th block), is it the same as the 5th block which the FullNode is having, If the Fullnode has a different 5th block than what publisher is publishing, It will add the token to double spend tokens table with DID1 & DID2 as the owners.

More possible exploits but not resolving for now(If we assume all the publisher nodes are under our control, no need to resolve this case):
case description:
let's say nodeA has 8 blocks. A has done double spend transaction, A -> B and A -> B' both with blocknumber 9. Now
B->C, C->D , D->E transactions has happend. So currently E is having 12th block. B' is having 9th block only. When Firstly, if FullNode sync from B', FullNode will get till 9th block. After that if E also publishes tokenchain with length 12. Fullnode checks, previous blockID of the incoming 10th block won't match with the already existing 9th block. so we will note down both DIDs as the same token holder for later reference while giving the tokens to them. If we do in this way any node can simply add a dummy block to their levelDB and publish with a different DID as a publisherDID then Fullnode treat it as a double spend transaction and note down both DIDs to distribute the tokens later.

Some more checks to discuss:

  1. lets say a Fullnode has received part token's genesis block, then fullnode should get it's parent tokenID and check whether it has already burnt block of the parent token with it, but the issue would be both, parent tokens burnt block and part tokens genesis block, will be get created at almost same point of time, so there is a chance that fullnode might not received in the same order in which they both get published, so this check might not be a good fit for the fullnodes.

…rom Did to PublisherDid for the SendTokenDetailsInfo struct; add error message column to Double spenttokenInfo table; handling double spend tokens in all 3 cases where fullnode has already more length token chain, or same length tokenchain or less length tokenchain.
…in fullnodeRBTTable; if synced token is a part token, then checks how many part tokens it's parent token is haviing if it is having more than 2 will add in multiple parenttokensTable.
…different blockIDs get published in case of regular transfer because of some double spending transaction.
…ock is migrated type in case of token transfer; adding some debug statements in validateSigner function.
…ming block number; added all checks for incoming txnBlock; added a seperate function by putting all 3 checks together; added functions to Validate the token content;
…omes a generic function to validate the token content;
…e changes in GetTokenContentAndValidate function;
…d to the publisher in ProcessRegular transfer; calling ValidateIncomingTokenBlock function in SyncFullTokenChainForFullNode function; modifying GetTokenBlockByNumber function to GetFullNodeTokenBlockByNumber function;
…ypes; handling the token owner check in case of smart contract token;
…etest tokens so that local tokens will be having token_level and token_number similar to faucet tokens or mainnet tokens;
… generatetest tokens so that local tokens will be having token_level and token_number similar to faucet tokens or mainnet tokens;"

This reverts commit ee02743.
@KryptSai
KryptSai marked this pull request as ready for review February 23, 2026 06:24

@arnabghose997 arnabghose997 left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ensure the errors are returned properly

Comment thread command/diagnostic.go Outdated
if len(elems) < 2 {
cmd.log.Error(fmt.Sprintf("Invalid token: %v", cmd.token))
return
if cmd.assetType == "rbt" || cmd.assetType == "RBT" {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lets' have only "rbt" as value to denote RBT Asset type

Comment thread core/wallet/token.go

// If no child tokens exist, return nil (as requested)
if len(childTokens) == 0 {
return nil, nil

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Two scenarios:

  • If this condition is expected to return an error, the error should be returned instead of nil
  • If this condition is not expected to return an error, instead of returning nil for list, an empty list should be returned

Comment thread core/fullnode.go
}

if err := c.w.AddFailedTokensToTable(info); err != nil {
c.log.Error("Failed to record failed token sync in DB", "token", tokenId, "error", err)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please return error here

Comment thread core/fullnode.go Outdated
c.log.Error("failed to sync token chain for token ", tokenId, "error", err)
return fmt.Errorf("failed to get latest block for token %s - may need sync", tokenId)

handled, _ := c.HandleSyncErrorAsDoubleSpent(err, tokenId, newEvent.AssetType, tokenType, newEvent.PublisherDID, previousOwner, txnBlockOwner, fmt.Sprintf("%s, %s both dids are claiming the same token or same did would have double spent the token,dual ownership issue", previousOwner, txnBlockOwner))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Return the error here

Comment thread core/fullnode.go
incomingBlockID := txnBlockID
publishersLatestBlockNumber := currentBlockNumber
//get Fullnode block whose block number is publisher side latest blockID
fullnodesideBlockBytes, err := c.w.GetFullNodeTokenBlockByNumber(tokenId, tokenType, publishersLatestBlockNumber)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The err is not returned here. Please return it

Comment thread core/fullnode.go
err := c.ReadTokenContentFromPSQL(tokenId, newEvent.AssetType)
if err != nil {
if err := c.AddTokenContentToPSQL(tokenId, newEvent.AssetType); err != nil {
c.log.Error("failed to add token's ipfs content to psql db, err: %v", err)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please return the error

Comment thread core/fullnode.go
}

if err := c.w.AddFailedTokensToTable(info); err != nil {
c.log.Error("Failed to record failed token sync in DB", "token", tokenId, "error", err)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please return the error here

Comment thread core/fullnode.go
}
// store double spent token info in DoubleSpentTokens table, and remove it from respective tokens table
err = c.StoreDoubleSpentTokenInfo(doubleSpentTokenInfo)
if err != nil {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error is not returned here

Comment thread core/fullnode.go Outdated
// Do all 3 checks, If any check fails, handle the failure here also just before adding the block to leveldb.
err = c.ValidateIncomingTokenBlock(*txnBlock, latestTokenBlock, tokenId, p, newEvent.AssetType)
if err != nil {
handled, _ := c.HandleSyncErrorAsDoubleSpent(err, tokenId, newEvent.AssetType, tokenType, newEvent.PublisherDID, previousOwner, txnBlockOwner, fmt.Sprintf("Incoming block %s is wrong,error %v", txnBlockID, err))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error is not returned here

Comment thread core/fullnode.go
err = c.ReadTokenContentFromPSQL(tokenId, newEvent.AssetType)
if err != nil {
if err := c.AddTokenContentToPSQL(tokenId, newEvent.AssetType); err != nil {
c.log.Error("failed to add token's ipfs content to psql db, err: %v", err)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

error is not returned here

…oken genuinity check at quorum side; return error from the HandleSyncErrorAsDoubleSpent handled in processReceivedTokenDetails function;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants