3
/*
 * Hi!
 * 
 * I'm sending this tiny library for you to check. This is the basis of the
 * on-chain signature collection solution I'm writing. It includes collecting
 * signatures for multi-signatures accounts as well as sharing transactions XDR
 * between signers.
 * 
 * The present module implement a messaging system to cut and send those datas
 * to the blockchain. It takes advantage of the fact that one can set the value
 * of the same data entry up to 100 times in one transaction. Actually, 99 as
 * the first operation is a payment of 0.0000001XLM to the destination address.
 * 
 * The signature/transaction sharing solution will makes use of dedicated
 * addresses where to send/collect those datas.
 * 
 * The reason I'm sending this to you is that the same messaging system could be
 * used to "abuse" the network. Well, actually you're the one to say if it would
 * be an abuse or not. And I'd like to know about that.
 * 
 * Using this library, one can send up to 6KB to the blockchain for a cost of
 * 100 stroops. This could open very interesting possibilities, like mailing
 * over Stellar, on-chain twitter-like services and even some basic blogging
 * capabilities as gzip would allow to emit articles up to 16KB.
 * 
 * I know you keep your archive light, so this may be a bit disruptive or
 * incompatible with your current goals for Stellar.
 * 
 * Just for you to know, I'm not the only one who found about this and some
 * people mentionned it indirectly on Galactic Talk. Umbrel implemented it in
 * a transaction-passing solution for papaya bot a few days ago. So this
 * possibility is not going to stay secret anyway.
 * 
 * I've been thinking about how to prevent abuse of this features and I didn't
 * found solution, really. It could be made that one have to use 99 different
 * data entry instead, but it would burden the account with cryptic labels
 * and the additionnal cost would be payed only once - it wouldn't do.
 * 
 * We could fix a maximum of 10 manageData operation or so per transaction.
 * But it would only make it a bit harder to code the exact same thing.
 * 
 * We could raise the fee for this kind of transaction, but it would make
 * multi-sig collection expansive which may not be desirable at all.
 * 
 * Another solution that would cut the possibility of on-chain signature
 * collection would be to forbid mixing manageData operations with other kind
 * of operations. But of course it would forbid a lot of precious innovations
 * as well.
 * 
 * So I would recommend to let it happen and see how much GB it's going to grow.
 * Eventually the message-passing transactions could be recognized as such and
 * deleted after a delay of a year or so, to prevent the archive to grow too
 * big. I assume that the cost of the GB of storage is low enough for that to
 * works.
 * 
 * Well anyway it's only the conclusion I came to. I assume to are in a better
 * position than me to say what we should do with that. And if you're open to it
 * I'd be glad to code some mailing over Stellar demonstrating the possibilities
 * of the network.
 * 
 * I really wonder if you did expect this kind of things when calling
 * developpers to innovate and surprise you with new uses of your blockchain?
 * 
 * Antoine alias Mister.Ticot
 */


(function (root, f) {
  'use_strict'
  if (typeof module !== 'undefined' && module.exports) module.exports = f(root)
  else root.messenger = f(root)
}(this, function(root) {
  'use_strict'
  if (typeof require !== 'undefined') var StellarSdk = require('stellar-sdk')
  else StellarSdk = root.StellarSdk

  /// Get Buffer constructor from StellarSdk
  var memo = new StellarSdk.Memo('hash', '0000000000000000000000000000000000000000000000000000000000000000')
  var Buffer = memo.value.__proto__.constructor

  var messenger = {}

  /**
   * Build a transaction to be signed by `senderAccount` to send `message` to
   * `destination`, with `object` as memo. The maximum size for `message` is
   * 6336 bytes.
   * 
   * The cost of emission is 1 stroop + 1 stroop for each 64 bytes to send.
   * 
   * @param {AccountResponse} senderAccount The Account object as return by `StellarSdk.Server.loadAccount()`
   * @param {String} destination The publicKey where to send the message
   * @param {String} object The memo of the transaction (max. 28 bytes)
   * @param {String} message The message to be send
   * @return {Transaction} A StellarSdk Transaction object
   */
  messenger.send = function (senderAccount, destination, object, message) {
    var transaction = new StellarSdk.TransactionBuilder(senderAccount)
    if (object.length > 28) console.log('Warning: object will be truncated.')
    transaction.addMemo(new StellarSdk.Memo('text', object.substr(0,28)))
    transaction.addOperation(StellarSdk.Operation.payment({
      destination: destination,
      asset: StellarSdk.Asset.native(),
      amount: '0.0000001'
    }))
    var array = new Buffer(message)
    if (array.length > 99*64) console.log('Warning: message will be truncated.')
    for (let i = 0; i < 99; i++) {
      const chunk = array.slice(i*64, i*64+64)
      if (chunk.length === 0) break
      transaction.addOperation(StellarSdk.Operation.manageData({
        name: 'Send',
        value: chunk
      }))
    }
    return transaction.build()
  }

  /**
   * Return the message object from `txHash`, using `server` to fetch datas.
   * 
   * @param {Server} server A StellarSdk Server object
   * @param {String} txHash A transaction hash
   * @return {Object} A message object with `sender`, `object`, `date` and
   *     `message` fields.
   */
  messenger.read = function(server, txHash) {
    var caller = server.transactions()
    return caller.transaction(txHash).call()
      .then(function(answer) {
        return { 
          sender: answer.source_account,
          object: answer.memo, 
          date: answer.created_at,
          message: extractMessage(answer)
        }
      })
  }

  function extractMessage (txCallAnswer) {
    if (txCallAnswer.operation_count < 2) return null
    var array = []
    var tx = new StellarSdk.Transaction(txCallAnswer.envelope_xdr)
    tx.operations.forEach(entry => {
      if (entry.type === 'manageData' && entry.name === 'Send') {
        console.log(entry.value)
        array.push(entry.value)
      }
    })
    return Buffer.concat(array).toString()
  }
}))
2

I can't speak for the SDF, but since you are asking about this, you probably already have guessed that this is not intended functionality. As such, I wouldn't rely on this behavior being there forever for any particular product you're developing. I would expect this to be addressed if it ever became a widespread problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.