ergoplatform / ergo

Ergo protocol description & reference client implementation
https://ergoplatform.org/
Creative Commons Zero v1.0 Universal
503 stars 170 forks source link

Check spending of tokens created in offchain transaction #1448

Open kushti opened 3 years ago

kushti commented 3 years ago

Reportedly, spending of a token created in UTXO which is offchain results in:

invalidated. Cause: For every token, its amount in outputs should not exceed its amount in inputs. 157f887f6698d1ba643990b93ea7bb1474918f56a24fcc899a4b0630fb85d685: Amount in = -1, out = 1. Allowed new asset = ByteArrayWrapper[5165884FF23345F374019C86016AE409C9E6E444357DEA5CFF3ED3BF19AC7D97], out = ByteArrayWrapper[870BECF7B15D6345DC746078306CAFA5D05E5B769580DE35BFA9035E6A300179]

weskinner commented 3 years ago

Steps to reproduce:

var superagent = require('superagent')

async function reproduce() {
  const address = "9hCqMZy97mi5qooyKzEWSJB4dcdCBoY4FRykNrcNy3wqcgZ4ayH"

  const aliceRequest = {
    "requests": [
      {
        "address": address,
        "ergValue": 1000000,
        "amount": 1,
        "name": "Alice Test",
        "description": "Alice NFT for Testing",
        "decimals": 0
      }
    ],
    "fee": 2000000
  }

  const bobRequest = {
    "requests": [
      {
        "address": address,
        "ergValue": 1000000,
        "amount": 1,
        "name": "Bob Test",
        "description": "Bob NFT for Testing",
        "decimals": 0
      }
    ],
    "fee": 2000000
  }

  let res = await superagent.post("http://localhost:9052/wallet/transaction/send")
    .set("api_key", "****")
    .send(aliceRequest)

  console.log("txn id",res.body)

  res = await superagent.post("http://localhost:9052/wallet/transaction/send")
    .set("api_key", "****")
    .send(aliceRequest)

  console.log("txn id",res.body)
}

reproduce()

output was:

txn id 4c30b2a1719f2512b778854118016c0c14acde75e5cc98360f773ff887400b31
txn id 53101bb90a2fbf7a9f2aabf0a6088f05f63c3501eb9b6149db1f228bb0f69a57

node logs:

13:53:13.640 DEBUG [r.default-dispatcher-3341] o.e.n.UtxoNodeViewHolder - Unconfirmed transaction ErgoTransaction(id: 4c30b2a1719f2512b778854118016c0c14acde75e5cc98360f773ff887400b31, inputs: [{"boxId":"81e2cd36a94070d9a8cf9ed634b1e8e1d42fd2f99bceac5dfe353c388db1894a","spendingProof":{"proofBytes":"666863ca716726da6a45c1cff50d5ed59dd0aa217eb759d0225bd99615b8369ced5fd9629ccfc5a2b3fb2727a7234dd0c23ca01da5919393","extension":{}}}], outputs: [{"boxId":"ae5b02d9b81562daa123bf912aaf4880e87f99b2d7b9505bfd83b39521e92172","value":1000000,"ergoTree":"0008cd0361b3affae9988e577fc9003b478b825c46a122bc03e58f08592d13013a861812","assets":[{"tokenId":"81e2cd36a94070d9a8cf9ed634b1e8e1d42fd2f99bceac5dfe353c388db1894a","amount":1}],"creationHeight":577890,"additionalRegisters":{"R4":"0e0a416c6963652054657374","R5":"0e15416c696365204e465420666f722054657374696e67","R6":"0e0130"},"transactionId":"4c30b2a1719f2512b778854118016c0c14acde75e5cc98360f773ff887400b31","index":0},{"boxId":"25eb9b23292007f41c866ab40e542fdaaea71a57142d5c80a796291986ba06a1","value":2000000,"ergoTree":"1005040004000e36100204a00b08cd0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ea02d192a39a8cc7a701730073011001020402d19683030193a38cc7b2a57300000193c2b2a57301007473027303830108cdeeac93b1a57304","assets":[],"creationHeight":577890,"additionalRegisters":{},"transactionId":"4c30b2a1719f2512b778854118016c0c14acde75e5cc98360f773ff887400b31","index":1},{"boxId":"d719403b68d99663674e637da761ad63b610bbc7dc9fcc256243792e4a7bc046","value":34697951900,"ergoTree":"0008cd0361b3affae9988e577fc9003b478b825c46a122bc03e58f08592d13013a861812","assets":[{"tokenId":"92bbb50b630acd2e0127c2c32bb90008af27a5804cfceb09d851d941388b3d5a","amount":1},{"tokenId":"c21592336f3b3f95fca25aaccf13b9d59119100033bc836fe76e9194f6d8b24c","amount":1},{"tokenId":"bbd35c0b4575c6dd265c29deb32503d7604da7c807b202893b8e950c99d8c3e9","amount":1},{"tokenId":"f93d64ff18035e39c46217b862fcc92d99fa66b36c58c1340186dd1ced08bef0","amount":484},{"tokenId":"16c29612aa7db1bf98ad126a37fff987aa8886e7e9d79ce5c09fda3f08d5d713","amount":1},{"tokenId":"27b5bf71e295dd852a536bfd8c8f8229e03c4ae929bceea63d6bba14fab32e5a","amount":1},{"tokenId":"8867d0896d1ea19bbd4b47232224607efd199c6e81605d9eb37f167097844e27","amount":1},{"tokenId":"7255903a666190aff098354279d9b920d0d6a4299bcb1389561538831b2a28b9","amount":1},{"tokenId":"ab3ce66bb6d6c41dfc0f3d39ac69f10688aadb98c784806c73752e91ceb47de5","amount":1},{"tokenId":"3124c2d48a3ce19f69d92f71eb4cf0ab41cfec2515f636d06fdd916a22838581","amount":1}],"creationHeight":577890,"additionalRegisters":{},"transactionId":"4c30b2a1719f2512b778854118016c0c14acde75e5cc98360f773ff887400b31","index":2}], size: 711) added to the memory pool

...

13:53:13.662 DEBUG [r.default-dispatcher-3339] o.e.n.UtxoNodeViewHolder - Transaction ErgoTransaction(id: 53101bb90a2fbf7a9f2aabf0a6088f05f63c3501eb9b6149db1f228bb0f69a57, inputs: [{"boxId":"ae5b02d9b81562daa123bf912aaf4880e87f99b2d7b9505bfd83b39521e92172","spendingProof":{"proofBytes":"371defb584b41ef025506170e160f39e991e27d40f5460be160c0b94a936cc80b7ac6bef3575ad2f86106c53dcfce9f3704f821d8062336b","extension":{}}},{"boxId":"d719403b68d99663674e637da761ad63b610bbc7dc9fcc256243792e4a7bc046","spendingProof":{"proofBytes":"dd0e42cb48cbb324526da3e15d8f890dac578ab5f74812e81654a2453014adffe295eee2f19741c26143b18f57a96e434936dd18140c2b29","extension":{}}}], outputs: [{"boxId":"aeb945a6fed7cabfe908231d13f894b598cc5eebddc769cee3b98cbe2af2ef8a","value":1000000,"ergoTree":"0008cd0361b3affae9988e577fc9003b478b825c46a122bc03e58f08592d13013a861812","assets":[{"tokenId":"81e2cd36a94070d9a8cf9ed634b1e8e1d42fd2f99bceac5dfe353c388db1894a","amount":1}],"creationHeight":577890,"additionalRegisters":{"R4":"0e0a416c6963652054657374","R5":"0e15416c696365204e465420666f722054657374696e67","R6":"0e0130"},"transactionId":"53101bb90a2fbf7a9f2aabf0a6088f05f63c3501eb9b6149db1f228bb0f69a57","index":0},{"boxId":"2fa7c23a635a928f58d18a5d3016362f25370ef77900065a2198f19b17cc78ab","value":2000000,"ergoTree":"1005040004000e36100204a00b08cd0279be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798ea02d192a39a8cc7a701730073011001020402d19683030193a38cc7b2a57300000193c2b2a57301007473027303830108cdeeac93b1a57304","assets":[],"creationHeight":577890,"additionalRegisters":{},"transactionId":"53101bb90a2fbf7a9f2aabf0a6088f05f63c3501eb9b6149db1f228bb0f69a57","index":1},{"boxId":"294a0184831cbc487588d102900736bb10967cd63f40388d9db1ca6b444d3b00","value":34695951900,"ergoTree":"0008cd0361b3affae9988e577fc9003b478b825c46a122bc03e58f08592d13013a861812","assets":[{"tokenId":"bbd35c0b4575c6dd265c29deb32503d7604da7c807b202893b8e950c99d8c3e9","amount":1},{"tokenId":"3124c2d48a3ce19f69d92f71eb4cf0ab41cfec2515f636d06fdd916a22838581","amount":1},{"tokenId":"27b5bf71e295dd852a536bfd8c8f8229e03c4ae929bceea63d6bba14fab32e5a","amount":1},{"tokenId":"92bbb50b630acd2e0127c2c32bb90008af27a5804cfceb09d851d941388b3d5a","amount":1},{"tokenId":"7255903a666190aff098354279d9b920d0d6a4299bcb1389561538831b2a28b9","amount":1},{"tokenId":"8867d0896d1ea19bbd4b47232224607efd199c6e81605d9eb37f167097844e27","amount":1},{"tokenId":"81e2cd36a94070d9a8cf9ed634b1e8e1d42fd2f99bceac5dfe353c388db1894a","amount":1},{"tokenId":"ab3ce66bb6d6c41dfc0f3d39ac69f10688aadb98c784806c73752e91ceb47de5","amount":1},{"tokenId":"c21592336f3b3f95fca25aaccf13b9d59119100033bc836fe76e9194f6d8b24c","amount":1},{"tokenId":"16c29612aa7db1bf98ad126a37fff987aa8886e7e9d79ce5c09fda3f08d5d713","amount":1},{"tokenId":"f93d64ff18035e39c46217b862fcc92d99fa66b36c58c1340186dd1ced08bef0","amount":484}],"creationHeight":577890,"additionalRegisters":{},"transactionId":"53101bb90a2fbf7a9f2aabf0a6088f05f63c3501eb9b6149db1f228bb0f69a57","index":2}], size: 803) invalidated. Cause: For every token, its amount in outputs should not exceed its amount in inputs. 53101bb90a2fbf7a9f2aabf0a6088f05f63c3501eb9b6149db1f228bb0f69a57: Amount in = 1, out = 2. Allowed new asset = ByteArrayWrapper[AE5B02D9B81562DAA123BF912AAF4880E87F99B2D7B9505BFD83B39521E92172], out = ByteArrayWrapper[81E2CD36A94070D9A8CF9ED634B1E8E1D42FD2F99BCEAC5DFE353C388DB1894A]
pragmaxim commented 3 years ago

Hi @weskinner thanks for the repro, bobRequest is not used there. How come no error happens in logs ?

UPDATE: This idea bellow is wrong !!!

However I think this is just a racing condition in a concurrent system so that the second transaction happens before this happens for the first : https://github.com/ergoplatform/ergo/blob/e68ce6180b13bffb024cf9e26c7c16a7be70a22c/src/main/scala/org/ergoplatform/nodeView/wallet/ErgoWalletActor.scala#L180

And NodeViewHolder is not responsive in this matter : https://github.com/ergoplatform/ergo/blob/9a6057faa69fe27a1df3c67ba61a2522c9b53622/src/main/scala/org/ergoplatform/nodeView/ErgoNodeViewHolder.scala#L57

Which means we would have to wait for SuccessfulTransaction[ErgoTransaction](tx) event in here : https://github.com/ergoplatform/ergo/blob/9a6057faa69fe27a1df3c67ba61a2522c9b53622/src/main/scala/org/ergoplatform/http/api/WalletApiRoute.scala#L159

In order to reply when the transaction is scanned and allow the client to execute the second transaction when the first is scanned, which IDK if it is wise. Otherwise I guess the client would have to wait some fraction of a second so that the transaction is scanned ...

weskinner commented 3 years ago

My bad. I've updated the code and was still able to reproduce the error with a 5 second delay before bobRequest:

var superagent = require('superagent')

async function reproduce() {
  const address = "9hCqMZy97mi5qooyKzEWSJB4dcdCBoY4FRykNrcNy3wqcgZ4ayH"

  const aliceRequest = {
    "requests": [
      {
        "address": address,
        "ergValue": 1000000,
        "amount": 1,
        "name": "Alice Test",
        "description": "Alice NFT for Testing",
        "decimals": 0
      }
    ],
    "fee": 2000000
  }

  const bobRequest = {
    "requests": [
      {
        "address": address,
        "ergValue": 1000000,
        "amount": 1,
        "name": "Bob Test",
        "description": "Bob NFT for Testing",
        "decimals": 0
      }
    ],
    "fee": 2000000
  }

  let res = await superagent.post("http://localhost:9052/wallet/transaction/send")
    .set("api_key", "75Y1fULtMlvCO3pY")
    .send(aliceRequest)

  console.log("txn id",res.body)

  setTimeout(async () => {
    res = await superagent.post("http://localhost:9052/wallet/transaction/send")
      .set("api_key", "75Y1fULtMlvCO3pY")
      .send(bobRequest)

    console.log("txn bob id",res.body)

  }, 5000);

}

reproduce()
pragmaxim commented 3 years ago

This is how it should be for the TX to be valid ... 2021-09-18_16-09

weskinner commented 3 years ago

This was determined to already be fixed in 4.0.14 and 4.0.15. It should be closed.