hyperledger / solang

Solidity Compiler for Solana and Polkadot
https://solang.readthedocs.io/
Apache License 2.0
1.25k stars 208 forks source link

Allow seeds to be passed into functions as string[] or string[][] #1547

Closed seanyoung closed 11 months ago

seanyoung commented 11 months ago

This fixes this Solidity and similar constructs.

import "solana-library/spl_token.sol";

contract c {

    // Invoke the token program to mint tokens to a token account, using a PDA as the mint authority
    function mintTo(address mint, address account, address authority, uint64 amount, bytes[] seeds) internal {
        // Prepare instruction data
        bytes instructionData = new bytes(9);
        instructionData[0] = uint8(7); // MintTo instruction index
        instructionData.writeUint64LE(amount, 1); // Amount to mint

        // Prepare accounts required by instruction
        AccountMeta[3] metas = [
            AccountMeta({pubkey: mint, is_writable: true, is_signer: false}),
            AccountMeta({pubkey: account, is_writable: true, is_signer: false}),
            AccountMeta({pubkey: authority, is_writable: true, is_signer: true})
        ];

        // Invoke the token program with prepared accounts and instruction data
        SplToken.tokenProgramId.call{accounts: metas, seeds: seeds}(instructionData);
    }
}

Fixes https://github.com/hyperledger/solang/issues/1433

LucasSte commented 11 months ago
import {AccountMeta} from 'solana';
            contract pda {
                address constant tokenProgramId = address"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
                address constant SYSVAR_RENT_PUBKEY = address"SysvarRent111111111111111111111111111111111";
                function test_bytes4(string[][] seeds) public {
                    bytes instr = new bytes(1);
                    instr[0] = 0x95;
                    AccountMeta[1] metas = [
                        AccountMeta({pubkey: SYSVAR_RENT_PUBKEY, is_writable: false, is_signer: false})
                    ];
                    tokenProgramId.call{seeds: seeds, accounts: metas}(instr);
                }
            }

Is this expected?

error: conversion from string[][] to bytes slice slice not possible
   ┌─ /Users/lucasste/Documents/solang/examples/test.sol:11:48
   │
11 │                     tokenProgramId.call{seeds: seeds, accounts: metas}(instr);
   │
LucasSte commented 11 months ago
            import {AccountMeta} from 'solana';
            contract pda {
                address constant tokenProgramId = address"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
                address constant SYSVAR_RENT_PUBKEY = address"SysvarRent111111111111111111111111111111111";
                function test(bytes[] dyn, address[] addr, bytes5[] b5) public {
                    bytes instr = new bytes(1);
                    instr[0] = 0x95;
                    AccountMeta[1] metas = [
                        AccountMeta({pubkey: SYSVAR_RENT_PUBKEY, is_writable: false, is_signer: false})
                    ];
                    bytes3 foo = "foo";
                    string foo2 = "123456";

                    tokenProgramId.call{seeds: [ dyn, addr, b5, [foo, foo2] ], accounts: metas}(instr);
                }
            }

Is this expected (part 2)?

error: conversion from string to bytes not possible
   ┌─ /Users/lucasste/Documents/solang/examples/test.sol:15:71
   │
15 │                     tokenProgramId.call{seeds: [ dyn, addr, b5, [foo, foo2] ], accounts: metas}(instr);
   │
LucasSte commented 11 months ago

Strings should be allowed, shouldn't they?

            import {AccountMeta} from 'solana';
            contract pda {
                address constant tokenProgramId = address"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
                address constant SYSVAR_RENT_PUBKEY = address"SysvarRent111111111111111111111111111111111";
                function test(bytes[] dyn, string[] addr, bytes5[] b5) public {
                    bytes instr = new bytes(1);
                    instr[0] = 0x95;
                    AccountMeta[1] metas = [
                        AccountMeta({pubkey: SYSVAR_RENT_PUBKEY, is_writable: false, is_signer: false})
                    ];
                    bytes3 foo = "foo";
                    bytes6 foo2 = "123456";

                    tokenProgramId.call{seeds: [ dyn, addr, b5, [foo, foo2] ], accounts: metas}(instr);
                }
            }
error: type string found where array bytes slice expected
   ┌─ /Users/lucasste/Documents/solang/examples/test.sol:14:55
   │
14 │                     tokenProgramId.call{seeds: [ dyn, addr, b5, [foo, foo2] ], accounts: metas}(instr);
   │
seanyoung commented 11 months ago

Type string is now allowed as a seed

LucasSte commented 11 months ago
import {AccountMeta} from 'solana';
            contract pda {
                address constant tokenProgramId = address"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
                address constant SYSVAR_RENT_PUBKEY = address"SysvarRent111111111111111111111111111111111";
                function test(bytes[] dyn, address[] addr, bytes5[] b5, string f) public {
                    bytes instr = new bytes(1);
                    instr[0] = 0x95;
                    AccountMeta[1] metas = [
                        AccountMeta({pubkey: SYSVAR_RENT_PUBKEY, is_writable: false, is_signer: false})
                    ];
                    bytes3 foo = "foo";
                    tokenProgramId.call{seeds: [ dyn, addr, b5, [f] ], accounts: metas}(instr);
                }
            }

Is this correct?

thread 'main' panicked at 'internal error: entered unreachable code', src/emit/expression.rs:2123:14
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
LucasSte commented 11 months ago

I also tested the following example, but I am not confident it is going to be a common construct, so perhaps we should not worry about it.

import {AccountMeta} from 'solana';
            contract pda {
                address constant tokenProgramId = address"TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA";
                address constant SYSVAR_RENT_PUBKEY = address"SysvarRent111111111111111111111111111111111";
                function test_bytes(bytes[][] seeds) public {
                    bytes instr = new bytes(1);
                    instr[0] = 0x95;
                    AccountMeta[1] metas = [
                        AccountMeta({pubkey: SYSVAR_RENT_PUBKEY, is_writable: false, is_signer: false})
                    ];
                    tokenProgramId.call{seeds: [seeds[0], seeds[1]], accounts: metas}(instr);
                }
            }
error: type bytes[] found where array expected
   ┌─ /Users/lucasste/Documents/solang/examples/test.sol:13:49
   │
13 │                     tokenProgramId.call{seeds: [seeds[0], seeds[1]], accounts: metas}(instr);
   │                                                 ^^^^^^^^

error: type bytes[] found where array expected
   ┌─ /Users/lucasste/Documents/solang/examples/test.sol:13:59
   │
13 │                     tokenProgramId.call{seeds: [seeds[0], seeds[1]], accounts: metas}(instr);
   │                                                           ^^^^^^^^
seanyoung commented 11 months ago

@LucasSte I've added the two cases you found as a test cases

seanyoung commented 11 months ago

As discussed in the standup, it would be great to have those SAFETY comments, explaining our thoughts around why all those arguments to the LLVM GEP builder are correct. But it's not only in this place, I'll leave it up to you.

What do you think of the SAFETY comments I've just added?

xermicus commented 11 months ago

What do you think of the SAFETY comments I've just added?

Great, this is exactly what I meant, thanks!

seanyoung commented 11 months ago

I tested it locally and couldn't find any problem. Codewise, everything looks good. I have only one last question.

Should there be an integration test for this feature?

I've added a simple test.

This will need re-writing for your branch.

There is also a fix for the "blockhash not found" issue (at least it fixes it locally)