One address to redirect to several addresses.

As the Title says, “One address to redirect to multiple addresses.”
I’m trying to find out if this functionality is possible. I didn’t see anything in the whitepaper about it but I think it would be a great opportunity that traditional banks don’t offer :grinning:

It’s possible, but I don’t think anyone has implemented it yet. Off the top of my head, maybe it would be implemented as a singleton that contains a list of addresses and payout percentages, both of which could then be updated with a spend of the singleton. Payouts could also be done by spending the singleton.

Good question! I think we could achieve this by creating a coin that when it is spent, it will create multiple coins for multiple addresses.

The puzzle p2_addresses.clsp could look like this:

(mod (PUZZLE_HASHES total_amount amount)
    (include condition_codes.clib)

    (defun pay_to (puzzle_hashes amount)
        (if (l puzzle_hashes)
        (c 
            (list CREATE_COIN (f puzzle_hashes) amount)
            (pay_to (r puzzle_hashes) amount)
        )
        ()
        )
    )

    (c
        (list ASSERT_MY_AMOUNT total_amount)
        (pay_to PUZZLE_HASHES amount)
    )
)

Then we can curry in the list of puzzle hashes that we want to redirect. For example, if we want to send to three addresses:

(
   0x79a4034c96a1c841bde9fcaa719879b723f8e6fe21f4f8d33da3105590abe39e
   0x9911c986fa81f7db76f6b110cee66deb11690b67f204fe290b3396b0824f467a
   0xca2d649e52b48d7da1d29ac661c99385422b2bc4cfdaaad97fde98f57a9cf526
)
❯ cdv clsp curry ./p2_addresses.clsp.hex -a '(0x79a4034c96a1c841bde9fcaa719879b723f8e6fe21f4f8d33da3105590abe39e 0x9911c986fa81f7db76f6b110cee66deb11690b67f204fe290b3396b0824f467a 0xca2d649e52b48d7da1d29ac661c99385422b2bc4cfdaaad97fde98f57a9cf526)'
(a (q 2 (q 4 (c 4 (c 11 ())) (a 14 (c 2 (c 5 (c 23 ()))))) (c (q 73 51 2 (i (l 5) (q 4 (c 10 (c 9 (c 11 ()))) (a 14 (c 2 (c 13 (c 11 ()))))) ()) 1) 1)) (c (q 0x79a4034c96a1c841bde9fcaa719879b723f8e6fe21f4f8d33da3105590abe39e 0x9911c986fa81f7db76f6b110cee66deb11690b67f204fe290b3396b0824f467a 0xca2d649e52b48d7da1d29ac661c99385422b2bc4cfdaaad97fde98f57a9cf526) 1))

We can spend a coin with the puzzle above and it should send three coins to those three addresses:

❯ brun '(a (q 2 (q 4 (c 4 (c 11 ())) (a 14 (c 2 (c 5 (c 23 ()))))) (c (q 73 51 2 (i (l 5) (q 4 (c 10 (c 9 (c 11 ()))) (a 14 (c 2 (c 13 (c 11 ()))))) ()) 1) 1)) (c (q 0x79a4034c96a1c841bde9fcaa719879b723f8e6fe21f4f8d33da3105590abe39e 0x9911c986fa81f7db76f6b110cee66deb11690b67f204fe290b3396b0824f467a 0xca2d649e52b48d7da1d29ac661c99385422b2bc4cfdaaad97fde98f57a9cf526) 1))' '(30 10)'
((73 30) (51 0x79a4034c96a1c841bde9fcaa719879b723f8e6fe21f4f8d33da3105590abe39e 10) (51 0x9911c986fa81f7db76f6b110cee66deb11690b67f204fe290b3396b0824f467a 10) (51 0xca2d649e52b48d7da1d29ac661c99385422b2bc4cfdaaad97fde98f57a9cf526 10))

The solution of the coin is (30 10). 30 is the amount of the p2_addresses coin and 10 is the amount that we want to send to each new coin.

1 Like

very good the code is automatically adapted to the number of addresses?
3 addresses = / 3
4 adresses = /4
Etc…

then what happens if 1 XCH is divided into 3? who gets the latest mojo?

very good the code is automatically adapted to the number of addresses?

Yes, our code can handle any number of addresses. However, the bigger the solution and the conditions, the more CLVM cost associated. So it’s always better to only do necessary tasks on the blockchain (e.g., do most computations in the python driver code).

Here are some good references:

You can check the cost with brun as well:

❯ brun --cost '(a (q 2 (q 4 (c 4 (c 11 ())) (a 14 (c 2 (c 5 (c 23 ()))))) (c (q 73 51 2 (i (l 5) (q 4 (c 10 (c 9 (c 11 ()))) (a 14 (c 2 (c 13 (c 11 ()))))) ()) 1) 1)) (c (q 0x79a4034c96a1c841bde9fcaa719879b723f8e6fe
21f4f8d33da3105590abe39e 0x9911c986fa81f7db76f6b110cee66deb11690b67f204fe290b3396b0824f467a 0xca2d649e52b48d7da1d29ac661c99385422b2bc4cfdaaad97fde98f57a9cf526) 1))' '(30 10)'
cost = 5285...

So, let’s you curry-in 30 addresses, the cost will increase!

❯ cdv clsp curry ./p2_addresses.clsp.hex -a '(0x79a4034c96a1c841bde9fcaa719879b723f8e6fe21f4f8d33da3105590abe39e 0x9911c986fa81f7db76f6b110cee66deb11690b67f204fe290b3396b0824f467a 0xca2d649e52b48d7da1d29ac661c99385422b2bc4cfdaaad97fde98f57a9cf526 0x79a4034c96a1c841bde9fcaa719879b723f8e6fe21f4f8d33da3105590abe39e 0x9911c986fa81f7db76f6b110cee66deb11690b67f204fe290b3396b0824f467a ...)'
(a (q 2 (q 4 (c 4 (c 11 ())) (a 14 (c 2 (c 5 (c 23 ()))))) (c (q 73 51 2 (i (l 5) (q 4 (c 10 (c 9 (c 11 ()))) (a 14 (c 2 (c 13 (c 11 ()))))) ()) 1) 1)) (c (q 0x79a4034c96a1c841bde9fcaa719879b723f8e6fe21f4f8d33da3105590abe39e 0x9911c986fa81f7db76f6b110cee66deb11690b67f204fe290b3396b0824f467a 0xca2d649e52b48d7da1d29ac661c99385422b2bc4cfdaaad97fde98f57a9cf526 0x79a4034c96a1c841bde9fcaa719879b723f8e6fe21f4f8d33da3105590abe39e 0x9911c986fa81f7db76f6b110cee66deb11690b67f204fe290b3396b0824f467a...) 1))

❯ brun --cost $(cdv clsp curry ./p2_addresses.clsp.hex -a '(0x79a4034c96a1c841bde9fcaa719879b723f8e6fe21f4f8d33da3105590abe39e 0x9911c986fa81f7db76f6b110cee66deb11690b67f204fe290b3396b0824f467a 0xca2d649e52b48d7da1d29ac661c99385422b2bc4cfdaaad97fde98f57a9cf526 0x79a4034c96a1c841bde9fcaa719879b723f8e6fe21f4f8d33da3105590abe39e 0x9911c986fa81f7db76f6b110cee66deb11690b67f204fe290b3396b0824f467a...)') '(30 1)'
cost = 38360...

then what happens if 1 XCH is divided into 3? who gets the latest mojo?

In our code, mojos are specified inside the solution, so your driver code should make sure the calculation is correct otherwise the spend might not be valid. For example, you couldn’t spend one 30-mojo coin and create five 10-mojo coins).

But if there is any mojos unused, those mojos will be fees for our beloved farmers.

A fee is created when the sum of the CREATE_COIN outputs is less than the amount of the coin that is being spent.

1 Like

I like the mojo for farmer :slight_smile:

1 Like

There is no verification for total_amount = amount * #curried_puzzhashes is this puzzle or am I wrong?

2 Likes

Good catch! You are right that there’s no check in this chialisp code (as I expect that to be done in the driver code). In the production, you should add more verification depending on your use cases. Most importantly is something like AGG_SIG_ME which will make sure that the values in solution are not tampered by anyone.

The example is just to show how we can model the concept of redirecting mojos to multiple addresses. The coin itself could be an ephemeral coin that is created and spent within the same block!

1 Like

Do you have any python code?
when I to try face error:

cdv encode a8886c13970eabd43956d3ba7729348641da20dbe5d08c4c90d20e42738072fb --prefix txch
txch14zyxcyuhp64agw2k6wa8w2f5seqa5gxmuhggcnys6g8yyuuqwtasung7vh
chia wallet send -a 0.3 -t txch14zyxcyuhp64agw2k6wa8w2f5seqa5gxmuhggcnys6g8yyuuqwtasung7vh
cdv rpc coinrecords --by puzhash a8886c13970eabd43956d3ba7729348641da20dbe5d08c4c90d20e42738072fb
opc ‘(300000000000,100000000000)’

(“{‘error’: 'Failed to include transaction "
‘7ad2e63272ca05780744b8b4486b3ee617dfc19842128f8545a526fc041170a9, error ’
"GENERATOR_RUNTIME_ERROR’, ‘success’: False}”)

{
“coin_spends”: [
{
“coin”: {
“parent_coin_info”: “36dc65c2d183ed5cb3696d8c9bdfc4d4da2a76d0d6771d82821ba6168113d1c5”,
“puzzle_hash”: “a8886c13970eabd43956d3ba7729348641da20dbe5d08c4c90d20e42738072fb”,
“amount”: 300000000000
},
“puzzle_reveal”: “ff02ffff01ff02ffff01ff04ffff04ff04ffff04ff0bff808080ffff02ff0effff04ff02ffff04ff05ffff04ff17ff808080808080ffff04ffff01ff49ff33ff02ffff03ffff07ff0580ffff01ff04ffff04ff0affff04ff09ffff04ff0bff80808080ffff02ff0effff04ff02ffff04ff0dffff04ff0bff808080808080ff8080ff0180ff018080ffff04ffff01ffa079a4034c96a1c841bde9fcaa719879b723f8e6fe21f4f8d33da3105590abe39effa09911c986fa81f7db76f6b110cee66deb11690b67f204fe290b3396b0824f467affa0ca2d649e52b48d7da1d29ac661c99385422b2bc4cfdaaad97fde98f57a9cf52680ff018080”,
“solution”: “ff993330303030303030303030302c31303030303030303030303080”
}
],
“aggregated_signature”: “c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000”
}

Besides brun, another easy way to debug your spend bundle is to use cdv inspect spendbundles <json file> -db

# bad spend bundle
❯ cdv inspect spendbundles ./bad-spend.json -db 
[{"aggregated_signature": ...]

Debugging Information
---------------------
...

brun -y main.sym '(a ...)' '("300000000000,100000000000")'
Traceback (most recent call last):
  ...
ValueError: ('path into atom', '80')

So you will notice the that the solution looks wrong.

You should do opc '(300000000000 100000000000)' without ,.

❯ opc '(300000000000 100000000000)'
ff8545d964b800ff85174876e80080

Update the solution in your spend bundle and it should work.

❯ cdv inspect spendbundles ./good-spend.json -db
[{"aggregated_signature": ...]

Debugging Information
---------------------
...

brun -y main.sym '(a (q 2 (q 4 (c 4 (c 11 ())) (a 14 (c 2 (c 5 (c 23 ()))))) (c (q 73 51 2 (i (l 5) (q 4 (c 10 (c 9 (c 11 ()))) (a 14 (c 2 (c 13 (c 11 ()))))) ()) 1) 1)) (c (q 0x79a4034c96a1c841bde9fcaa719879b723f8e6fe21f4f8d33da3105590abe39e 0x9911c986fa81f7db76f6b110cee66deb11690b67f204fe290b3396b0824f467a 0xca2d649e52b48d7da1d29ac661c99385422b2bc4cfdaaad97fde98f57a9cf526) 1))' '(0x45d964b800 0x174876e800)'

((ASSERT_MY_AMOUNT 0x45d964b800) (CREATE_COIN 0x79a4034c96a1c841bde9fcaa719879b723f8e6fe21f4f8d33da3105590abe39e 0x174876e800) (CREATE_COIN 0x9911c986fa81f7db76f6b110cee66deb11690b67f204fe290b3396b0824f467a 0x174876e800) (CREATE_COIN 0xca2d649e52b48d7da1d29ac661c99385422b2bc4cfdaaad97fde98f57a9cf526 0x174876e800))

grouped conditions:

  (ASSERT_MY_AMOUNT 0x45d964b800)

  (CREATE_COIN 0x79a4034c96a1c841bde9fcaa719879b723f8e6fe21f4f8d33da3105590abe39e 0x174876e800)
  (CREATE_COIN 0x9911c986fa81f7db76f6b110cee66deb11690b67f204fe290b3396b0824f467a 0x174876e800)
  (CREATE_COIN 0xca2d649e52b48d7da1d29ac661c99385422b2bc4cfdaaad97fde98f57a9cf526 0x174876e800)

-------
...

================================================================================

aggregated signature check pass: True
pks: []
msgs: []
  msg_data: []
  coin_ids: []
  add_data: []
signature: c00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
None

Have fix.
Very Thanks

Could you speak a bit to the threats you would address with AGG_SIG_ME?

I was thinking of a change that would curry in both the addresses and the percentage for each and make it fixed in the puzzle hash. I can think of two downsides to that. A malicious caller could potentially invoke the puzzle dozens of times and dust the recipients or intentionally cause the remainders for unequal distributions to be given to farmers. Having a minimum spend size in the puzzle and/or having remainders remain secured by the puzzlehash would seem to work.

I guess what I’m saying is I want to simplify the puzzle for a particular use case without oversimplifying out the security. It seems like a spend that can be invoked by anyone, but already has all the most important arguments curried in, seems pretty safe.

can you share the chialisp you have in mind?

This is a super-simplified example, with a hardcoded 2 for 2 addresses. I guess my root question is: does it matter that we protect against unauthorized spends if the behavior of the spend can only cause one outcome?

The use case I have in mind is a royalty-distributing piggy bank of sorts. Writing code that “works” is pretty easy, but I want to make sure I am considering the threats correctly.

(mod (PUZZLE_HASHES total_amount)
    (include condition_codes.clib)

    (defun pay_to (puzzle_hashes total_amount)
        (if (l puzzle_hashes)
        (c 
            (list CREATE_COIN (f puzzle_hashes) (f (divmod total_amount 2)))
            (pay_to (r puzzle_hashes) total_amount)
        )
        ()
        )
    )

    (c
        (list ASSERT_MY_AMOUNT total_amount)
        (pay_to PUZZLE_HASHES total_amount)
    )
)

AGG_SIG_ME helps with the followings:

  1. It guaranteea that only the private key owner can provide a valid signature given the public key (curried or hard-coded inside the puzzle).
  2. Although you can sign anything, usually you’d sign the values provided in the solution, so that nobody can change those values without invalidating the signature.

Currying values also helps because those values are embedded inside the puzzle which basically creates a new puzzle (hence, new puzzle hash). Currying implants part of solutions, so the puzzle is less adaptive.

Anyway, AGG_SIG_ME should always be used unless you can’t because you don’t know the public key in advance. This security tips and tricks should help with securing your coin.

Thanks, I’m following what AGG_SIG_ME does, but I’m specifically wondering if I had all the addresses and their percentage allocations hard-coded in if it would really matter who called it. I thought it might be handy to have an unsecured “sweeper” process that just watched the chain for activity on the puzzle hash address and just anonymously created a spend to sweep it according to the rules from the shared address to the individual addresses.

The other option I could think of is to allow anyone of the recipients’s keys to sign the bundle, but I definitely don’t want to require ALL of them to sign it.

Yes, that’d work since everything has been hard-coded into the puzzle. I think it’s called absorb spend which is how pool claims pool reward. You can also make the coin to be spendable in different ways (cases) or restrict when it can be spent by providing different conditions. Look at the pool member inner puzzle for some idea.

1 Like

While AGG_SIG_ME ensures it can only be called from that pubkey, I don’t think it adds much more security here. This coin can only be spent with one solution (per coin) and caller can’t affect the outcome. Sweeper idea makes sense and is a common pattern in UTXO model (check Secure the Bag).
You’ll be losing 1 mojo on odd amounts though, which you could add to first puzhash and then cycle puzzlehashes accordingly or leave the mojo to the farmer.

2 Likes

Thanks for more information. Is there a design pattern name? Is it the same as absorb spend?