Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ct Account query precompile #2038

Merged
merged 10 commits into from
Jan 15, 2025
Merged
1 change: 1 addition & 0 deletions app/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -722,6 +722,7 @@ func New(
app.IBCKeeper.ConnectionKeeper,
app.IBCKeeper.ChannelKeeper,
app.AccountKeeper,
app.ConfidentialTransfersKeeper,
ctkeeper.NewMsgServerImpl(app.ConfidentialTransfersKeeper),
); err != nil {
panic(err)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# Tests for querying ct account via precompile. These tests depend on initialize_account_tests running and passing.
- name: Test querying confidential token account via precompile
inputs:
# Setup test account
- cmd: printf "12345678\n" | seid keys add ctquerytest
- cmd: printf "12345678\n" | seid keys show -a admin
env: ADMIN_ADDR
- cmd: printf "12345678\n" | seid keys show -a ctquerytest
env: TEST_ADDR
- cmd: printf "12345678\n" | seid tx bank send $ADMIN_ADDR $TEST_ADDR 100000000uatom -b block --fees 2000usei --chain-id sei -y --output json | jq -r ".code"
- cmd: printf "12345678\n" | seid tx bank send $ADMIN_ADDR $TEST_ADDR 1sei -b block --fees 2000usei --chain-id sei -y --output json | jq -r ".code"

# Initialize confidential token account for denom uatom for admin
- cmd: printf "12345678\n" | seid tx ct init-account uatom --from ctquerytest --fees 4000usei --chain-id sei -b block -y --output json | jq -r ".code"
env: INIT_ACCOUNT_CODE

# Deposit to the confidential token account
- cmd: printf "12345678\n" | seid tx ct deposit 500000uatom --from ctquerytest --fees 4000usei --chain-id sei -b block -y --output json | jq -r ".code"
env: DEPOSIT_CODE

# Query the account state
- cmd: printf "12345678\n" | seid q ct account uatom $TEST_ADDR --output json
env: ACCOUNT_STATE
- cmd: echo $ACCOUNT_STATE | jq -r ".pending_balance_credit_counter"
env: PENDING_BALANCE_COUNTER
- cmd: echo $ACCOUNT_STATE | jq -r ".decryptable_available_balance"
env: AVAILABLE_BALANCE_CIPHER

# Query the account to verify the new account state via precompile
- cmd: cast call 0x0000000000000000000000000000000000001010 "account(string,string)((bytes,bytes,bytes,uint32,bytes,string))" $TEST_ADDR uatom
env: ACCOUNT_STATE_PRECOMPILE
- cmd: echo $ACCOUNT_STATE_PRECOMPILE | jq -R -r "split(\", \")[3]"
env: PENDING_BALANCE_COUNTER_PRECOMPILE
- cmd: echo $ACCOUNT_STATE_PRECOMPILE| jq -R -r "split(\", \")[5]"| sed -e s/\"\)//g -e s/\"//
env: AVAILABLE_BALANCE_CIPHER_PRECOMPILE
verifiers:
# Verify that the account exists after the instruction is executed.
- type: eval
expr: INIT_ACCOUNT_CODE == 0 or INIT_ACCOUNT_CODE == 38

# Verify that the deposit was successful
- type: eval
expr: DEPOSIT_CODE == 0

# Verify that the pending balance counter is the same in both queries
- type: eval
expr: PENDING_BALANCE_COUNTER == PENDING_BALANCE_COUNTER_PRECOMPILE

# Verify that the available balance is the same in both queries
- type: eval
expr: AVAILABLE_BALANCE_CIPHER == AVAILABLE_BALANCE_CIPHER_PRECOMPILE


4 changes: 4 additions & 0 deletions precompiles/common/expected_keepers.go
Original file line number Diff line number Diff line change
Expand Up @@ -139,3 +139,7 @@ type ConfidentialTransfersKeeper interface {
Withdraw(goCtx context.Context, req *cttypes.MsgWithdraw) (*cttypes.MsgWithdrawResponse, error)
CloseAccount(goCtx context.Context, req *cttypes.MsgCloseAccount) (*cttypes.MsgCloseAccountResponse, error)
}

type ConfidentialTransfersViewKeeper interface {
GetAccount(ctx sdk.Context, address string, denom string) (cttypes.Account, bool)
}
17 changes: 16 additions & 1 deletion precompiles/confidentialtransfers/CT.sol
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ interface ICT {
string denom,
string decryptableBalance,
uint32 pendingBalanceCreditCounter,
bytes availableBalance,
bytes availableBalance
) external returns (bool success);

function withdraw(
Expand All @@ -79,4 +79,19 @@ interface ICT {
string denom,
bytes proofs
) external returns (bool success);

// Queries
function account(
string addr,
string denom
) external view returns (CtAccount account);

struct CtAccount {
bytes publicKey; // serialized public key
bytes pendingBalanceLo; // lo bits of the pending balance
bytes pendingBalanceHi; // hi bits of the pending balance
uint32 pendingBalanceCreditCounter;
bytes availableBalance; // elgamal encoded balance
string decryptableAvailableBalance; // aes encoded balance
}
}
2 changes: 1 addition & 1 deletion precompiles/confidentialtransfers/abi.json
Original file line number Diff line number Diff line change
@@ -1 +1 @@
[{"inputs":[{"internalType":"string","name":"fromAddress","type":"string"},{"internalType":"string","name":"denom","type":"string"},{"internalType":"bytes","name":"publicKey","type":"bytes"},{"internalType":"string","name":"decryptableBalance","type":"string"},{"internalType":"bytes","name":"pendingBalanceLo","type":"bytes"},{"internalType":"bytes","name":"pendingBalanceHi","type":"bytes"},{"internalType":"bytes","name":"availableBalance","type":"bytes"},{"internalType":"bytes","name":"proofs","type":"bytes"}],"name":"initializeAccount","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"toAddress","type":"string"},{"internalType":"string","name":"denom","type":"string"},{"internalType":"bytes","name":"fromAmountLo","type":"bytes"},{"internalType":"bytes","name":"fromAmountHi","type":"bytes"},{"internalType":"bytes","name":"toAmountLo","type":"bytes"},{"internalType":"bytes","name":"toAmountHi","type":"bytes"},{"internalType":"bytes","name":"remainingBalance","type":"bytes"},{"internalType":"string","name":"decryptableBalance","type":"string"},{"internalType":"bytes","name":"proofs","type":"bytes"}],"name":"transfer","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"toAddress","type":"string"},{"internalType":"string","name":"denom","type":"string"},{"internalType":"bytes","name":"fromAmountLo","type":"bytes"},{"internalType":"bytes","name":"fromAmountHi","type":"bytes"},{"internalType":"bytes","name":"toAmountLo","type":"bytes"},{"internalType":"bytes","name":"toAmountHi","type":"bytes"},{"internalType":"bytes","name":"remainingBalance","type":"bytes"},{"internalType":"string","name":"decryptableBalance","type":"string"},{"internalType":"bytes","name":"proofs","type":"bytes"},{"components":[{"internalType":"string","name":"auditorAddress","type":"string"},{"internalType":"bytes","name":"encryptedTransferAmountLo","type":"bytes"},{"internalType":"bytes","name":"encryptedTransferAmountHi","type":"bytes"},{"internalType":"bytes","name":"transferAmountLoValidityProof","type":"bytes"},{"internalType":"bytes","name":"transferAmountHiValidityProof","type":"bytes"},{"internalType":"bytes","name":"transferAmountLoEqualityProof","type":"bytes"},{"internalType":"bytes","name":"transferAmountHiEqualityProof","type":"bytes"}],"internalType":"struct ICT.Auditor[]","name":"auditors","type":"tuple[]"}],"name":"transferWithAuditors","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"denom","type":"string"},{"internalType":"uint64","name":"amount","type":"uint64"}],"name":"deposit","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"denom","type":"string"},{"internalType":"string","name":"decryptableBalance","type":"string"},{"internalType":"uint32","name":"pendingBalanceCreditCounter","type":"uint32"},{"internalType":"bytes","name":"availableBalance","type":"bytes"}],"name":"applyPendingBalance","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"denom","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"decryptableBalance","type":"string"},{"internalType":"bytes","name":"remainingBalanceCommitment","type":"bytes"},{"internalType":"bytes","name":"proofs","type":"bytes"}],"name":"withdraw","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"denom","type":"string"},{"internalType":"bytes","name":"proofs","type":"bytes"}],"name":"closeAccount","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]
[{"inputs":[{"internalType":"string","name":"fromAddress","type":"string"},{"internalType":"string","name":"denom","type":"string"},{"internalType":"bytes","name":"publicKey","type":"bytes"},{"internalType":"string","name":"decryptableBalance","type":"string"},{"internalType":"bytes","name":"pendingBalanceLo","type":"bytes"},{"internalType":"bytes","name":"pendingBalanceHi","type":"bytes"},{"internalType":"bytes","name":"availableBalance","type":"bytes"},{"internalType":"bytes","name":"proofs","type":"bytes"}],"name":"initializeAccount","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"toAddress","type":"string"},{"internalType":"string","name":"denom","type":"string"},{"internalType":"bytes","name":"fromAmountLo","type":"bytes"},{"internalType":"bytes","name":"fromAmountHi","type":"bytes"},{"internalType":"bytes","name":"toAmountLo","type":"bytes"},{"internalType":"bytes","name":"toAmountHi","type":"bytes"},{"internalType":"bytes","name":"remainingBalance","type":"bytes"},{"internalType":"string","name":"decryptableBalance","type":"string"},{"internalType":"bytes","name":"proofs","type":"bytes"}],"name":"transfer","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"toAddress","type":"string"},{"internalType":"string","name":"denom","type":"string"},{"internalType":"bytes","name":"fromAmountLo","type":"bytes"},{"internalType":"bytes","name":"fromAmountHi","type":"bytes"},{"internalType":"bytes","name":"toAmountLo","type":"bytes"},{"internalType":"bytes","name":"toAmountHi","type":"bytes"},{"internalType":"bytes","name":"remainingBalance","type":"bytes"},{"internalType":"string","name":"decryptableBalance","type":"string"},{"internalType":"bytes","name":"proofs","type":"bytes"},{"components":[{"internalType":"string","name":"auditorAddress","type":"string"},{"internalType":"bytes","name":"encryptedTransferAmountLo","type":"bytes"},{"internalType":"bytes","name":"encryptedTransferAmountHi","type":"bytes"},{"internalType":"bytes","name":"transferAmountLoValidityProof","type":"bytes"},{"internalType":"bytes","name":"transferAmountHiValidityProof","type":"bytes"},{"internalType":"bytes","name":"transferAmountLoEqualityProof","type":"bytes"},{"internalType":"bytes","name":"transferAmountHiEqualityProof","type":"bytes"}],"internalType":"struct ICT.Auditor[]","name":"auditors","type":"tuple[]"}],"name":"transferWithAuditors","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"denom","type":"string"},{"internalType":"uint64","name":"amount","type":"uint64"}],"name":"deposit","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"denom","type":"string"},{"internalType":"string","name":"decryptableBalance","type":"string"},{"internalType":"uint32","name":"pendingBalanceCreditCounter","type":"uint32"},{"internalType":"bytes","name":"availableBalance","type":"bytes"}],"name":"applyPendingBalance","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"denom","type":"string"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"string","name":"decryptableBalance","type":"string"},{"internalType":"bytes","name":"remainingBalanceCommitment","type":"bytes"},{"internalType":"bytes","name":"proofs","type":"bytes"}],"name":"withdraw","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"denom","type":"string"},{"internalType":"bytes","name":"proofs","type":"bytes"}],"name":"closeAccount","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"addr","type":"string"},{"internalType":"string","name":"denom","type":"string"}],"name":"account","outputs":[{"components":[{"internalType":"bytes","name":"publicKey","type":"bytes"},{"internalType":"bytes","name":"pendingBalanceLo","type":"bytes"},{"internalType":"bytes","name":"pendingBalanceHi","type":"bytes"},{"internalType":"uint32","name":"pendingBalanceCreditCounter","type":"uint32"},{"internalType":"bytes","name":"availableBalance","type":"bytes"},{"internalType":"string","name":"decryptableAvailableBalance","type":"string"}],"internalType":"struct CtAccount","name":"ctAccount","type":"tuple"}],"stateMutability":"view","type":"function"}]
107 changes: 100 additions & 7 deletions precompiles/confidentialtransfers/ct.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
TransferWithAuditorsMethod = "transferWithAuditors"
WithdrawMethod = "withdraw"
CloseAccountMethod = "closeAccount"
AccountMethod = "account"
)

const (
Expand All @@ -34,9 +35,10 @@
var f embed.FS

type PrecompileExecutor struct {
evmKeeper pcommon.EVMKeeper
ctKeeper pcommon.ConfidentialTransfersKeeper
address common.Address
evmKeeper pcommon.EVMKeeper
ctViewKeeper pcommon.ConfidentialTransfersViewKeeper
ctKeeper pcommon.ConfidentialTransfersKeeper
address common.Address

InitializeAccountID []byte
DepositID []byte
Expand All @@ -45,15 +47,21 @@
TransferWithAuditorsID []byte
WithdrawID []byte
CloseAccountID []byte
AccountID []byte
}

func NewPrecompile(ctkeeper pcommon.ConfidentialTransfersKeeper, evmKeeper pcommon.EVMKeeper) (*pcommon.DynamicGasPrecompile, error) {
func NewPrecompile(
ctViewKeeper pcommon.ConfidentialTransfersViewKeeper,
ctKeeper pcommon.ConfidentialTransfersKeeper,
evmKeeper pcommon.EVMKeeper) (*pcommon.DynamicGasPrecompile, error) {

newAbi := pcommon.MustGetABI(f, "abi.json")

p := &PrecompileExecutor{
evmKeeper: evmKeeper,
ctKeeper: ctkeeper,
address: common.HexToAddress(CtAddress),
evmKeeper: evmKeeper,
ctViewKeeper: ctViewKeeper,
ctKeeper: ctKeeper,
address: common.HexToAddress(CtAddress),
}

for name, m := range newAbi.Methods {
Expand All @@ -72,6 +80,8 @@
p.WithdrawID = m.ID
case CloseAccountMethod:
p.CloseAccountID = m.ID
case AccountMethod:
p.AccountID = m.ID
}
}

Expand Down Expand Up @@ -121,6 +131,8 @@
return nil, 0, errors.New("cannot call ct precompile from staticcall")
}
return p.closeAccount(ctx, method, caller, args)
case AccountMethod:
return p.account(ctx, method, args)
}
return
}
Expand Down Expand Up @@ -745,3 +757,84 @@
remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper)
return
}

type CtAccount struct {
PublicKey []byte
PendingBalanceLo []byte
PendingBalanceHi []byte
PendingBalanceCreditCounter uint32
AvailableBalance []byte
DecryptableAvailableBalance string
}

func (p PrecompileExecutor) account(ctx sdk.Context, method *abi.Method, args []interface{}) (ret []byte, remainingGas uint64, rerr error) {
defer func() {
if err := recover(); err != nil {
ret = nil
remainingGas = 0
rerr = fmt.Errorf("%s", err)
return
}

Check warning on line 777 in precompiles/confidentialtransfers/ct.go

View check run for this annotation

Codecov / codecov/patch

precompiles/confidentialtransfers/ct.go#L773-L777

Added lines #L773 - L777 were not covered by tests
}()

if err := pcommon.ValidateArgsLength(args, 2); err != nil {
rerr = err
return
}

Check warning on line 783 in precompiles/confidentialtransfers/ct.go

View check run for this annotation

Codecov / codecov/patch

precompiles/confidentialtransfers/ct.go#L781-L783

Added lines #L781 - L783 were not covered by tests

addrString, ok := (args[0]).(string)
if !ok || addrString == "" {
rerr = errors.New("invalid address")
return
}

seiAddr, err := p.getValidSeiAddressFromString(ctx, addrString)
if err != nil {
rerr = err
return
}

denom, ok := args[1].(string)
if !ok || denom == "" {
rerr = errors.New("invalid denom")
return
}

account, found := p.ctViewKeeper.GetAccount(ctx, seiAddr.String(), denom)
if !found {
rerr = errors.New("account not found")
return
}

accountProto := cttypes.NewCtAccount(&account)
pendingBalanceLo, err := accountProto.PendingBalanceLo.Marshal()
if err != nil {
rerr = err
return
}

Check warning on line 814 in precompiles/confidentialtransfers/ct.go

View check run for this annotation

Codecov / codecov/patch

precompiles/confidentialtransfers/ct.go#L812-L814

Added lines #L812 - L814 were not covered by tests

pendingBalanceHi, err := accountProto.PendingBalanceHi.Marshal()
if err != nil {
rerr = err
return
}

Check warning on line 820 in precompiles/confidentialtransfers/ct.go

View check run for this annotation

Codecov / codecov/patch

precompiles/confidentialtransfers/ct.go#L818-L820

Added lines #L818 - L820 were not covered by tests

availableBalance, err := accountProto.AvailableBalance.Marshal()
if err != nil {
rerr = err
return
}

Check warning on line 826 in precompiles/confidentialtransfers/ct.go

View check run for this annotation

Codecov / codecov/patch

precompiles/confidentialtransfers/ct.go#L824-L826

Added lines #L824 - L826 were not covered by tests

ctAccount := &CtAccount{
PublicKey: accountProto.PublicKey,
PendingBalanceLo: pendingBalanceLo,
PendingBalanceHi: pendingBalanceHi,
PendingBalanceCreditCounter: accountProto.PendingBalanceCreditCounter,
AvailableBalance: availableBalance,
DecryptableAvailableBalance: accountProto.DecryptableAvailableBalance,
}

ret, rerr = method.Outputs.Pack(ctAccount)
remainingGas = pcommon.GetRemainingGas(ctx, p.evmKeeper)
return
}
Loading
Loading