Skip to content

Commit

Permalink
updated the invoke host function json api resource model for multi-fu…
Browse files Browse the repository at this point in the history
…nctions
  • Loading branch information
sreuland committed May 3, 2023
1 parent b1182b0 commit 08e731c
Show file tree
Hide file tree
Showing 20 changed files with 882 additions and 432 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/horizon.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ jobs:
env:
HORIZON_INTEGRATION_TESTS_ENABLED: true
HORIZON_INTEGRATION_TESTS_CORE_MAX_SUPPORTED_PROTOCOL: ${{ matrix.protocol-version }}
PROTOCOL_20_CORE_DEBIAN_PKG_VERSION: 19.8.1-1246.064a2787a.focal~soroban
PROTOCOL_20_CORE_DOCKER_IMG: sreuland/stellar-core:19.8.1-1246.064a2787a.focal-soroban
PROTOCOL_20_CORE_DEBIAN_PKG_VERSION: 19.9.1-1270.04f2a6d5c.focal~soroban
PROTOCOL_20_CORE_DOCKER_IMG: sreuland/stellar-core:19.9.1-1270.04f2a6d5c.focal-soroban
PROTOCOL_19_CORE_DEBIAN_PKG_VERSION: 19.5.0-1108.ca2fb0605.focal
PROTOCOL_19_CORE_DOCKER_IMG: stellar/stellar-core:19.5.0-1108.ca2fb0605.focal
PGHOST: localhost
Expand Down
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ xdr/Stellar-overlay.x \
xdr/Stellar-transaction.x \
xdr/Stellar-types.x \
xdr/Stellar-contract-env-meta.x \
xdr/Stellar-contract-meta.x \
xdr/Stellar-contract-spec.x \
xdr/Stellar-contract.x \
xdr/Stellar-internal.x \
xdr/Stellar-contract-config-setting.x

XDRGEN_COMMIT=80e38ef2a96489f6b501d4db3a350406e5aa3bab
XDRNEXT_COMMIT=2f16687fdf6f4bcfb56805e2035f69997f4b34c4
XDRNEXT_COMMIT=b721f812a353e15db286b6e1a3c095b9be9f4bd4

.PHONY: xdr xdr-clean xdr-update

Expand Down
335 changes: 226 additions & 109 deletions gxdr/xdr_generated.go

Large diffs are not rendered by default.

40 changes: 19 additions & 21 deletions protocols/horizon/operations/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -345,41 +345,39 @@ type LiquidityPoolWithdraw struct {
ReservesReceived []base.AssetAmount `json:"reserves_received"`
}

// InvokeHostFunction is the json resource representing a single smart contract
// function invocation operation, having type InvokeHostFunction.
//
// The model for InvokeHostFunction is intentionally simplified.
// Parameters - array of tuples of each function input parameter value and it's data type
// Function - name of contract function
// Footprint - base64 encoded string of it's xdr serialization.
// AssetBalanceChanges - array of asset balance changed records.
// InvokeHostFunction is the json resource representing a single InvokeHostFunctionOp.
// The model for InvokeHostFunction assimilates InvokeHostFunctionOp, but is simplified.
// Functions - list of contract function invocations performed.
// AssetBalanceChanges - array of asset balance changed records related to contract invocations in this host invocation.
// The asset balance change record is captured at ingestion time from the asset contract
// events present in tx meta. Only asset contract events that have a reference to classic account in
// either the 'from' or 'to' participants will be included here as an asset balance change.
// Any pure contract-to-contract events with no reference to classic accounts are not included,
// as there is no explicit model in horizon for contract addresses yet.

type InvokeHostFunction struct {
Base
Parameters []HostFunctionParameter `json:"parameters"`
Function string `json:"function"`
Footprint string `json:"footprint"`
AssetBalanceChanges []AssetContractBalanceChange `json:"asset_balance_changes"`
HostFunctions []HostFunction `json:"host_functions"`
AssetBalanceChanges []AssetContractBalanceChange `json:"asset_balance_changes"`

This comment has been minimized.

Copy link
@tsachiherman

tsachiherman May 3, 2023

Contributor

could you fix the indentation on this one ?

}

// InvokeHostFunction parameter model, intentionally simplified, Value
// just contains a base64 encoded string of the ScVal xdr serialization.
type HostFunctionParameter struct {
Value string `json:"value"`
Type string `json:"type"`
// HostFunction has the values specific to a single host function invocation
// Type - the type of host function, invoke_contract, create_contract, upload_wasm
// Parameters - array of key,value tuples for each function parameter.
// one key that will always be incluced is 'type' which will be one of:
// xdr.ScValTypeScv's ( Sym, I32, U32, U64, Bytes, B ) or 'n/a' or 'string'
type HostFunction struct {
Type string `json:"type"`
Parameters []map[string]string `json:"parameters"`
}

// Type - refers to the source SAC Event
//
// it can only be one of 'transfer', 'mint', 'clawback' or 'burn'
//
// From - this is classic account that asset balance was changed.
// To - this is the classic account that asset balance was changed, or if not applicable
// From - this is classic account that asset balance was changed,
// or absent if not applicable for function
// To - this is the classic account that asset balance was changed,
// or absent if not applicable for function
//
// for asset contract event type, it can be absent such as 'burn'
//
Expand All @@ -388,7 +386,7 @@ type HostFunctionParameter struct {
type AssetContractBalanceChange struct {
base.Asset
Type string `json:"type"`
From string `json:"from"`
From string `json:"from,omitempty"`
To string `json:"to,omitempty"`
Amount string `json:"amount"`
}
Expand Down
53 changes: 49 additions & 4 deletions services/horizon/internal/actions/operation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,37 @@ func TestInvokeHostFnDetailsInPaymentOperations(t *testing.T) {
1,
xdr.OperationTypeInvokeHostFunction,
[]byte(`{
"parameters": [],
"function": "fn",
"footprint": "",
"host_functions": [
{
"type": "invoke_contract",
"parameters": [
{
"value": "AAAADwAAAAdmbl9uYW1lAA==",
"type": "Sym"
},
{
"value": "AAAAAwAAAAI=",
"type": "U32"
}
]
},
{
"type": "create_contract",
"parameters": [
{
"from": "source_account",
"type": "string"
},
{
"salt": "123",
"type": "string"
}
]
},
{
"type": "upload_wasm"
}
],
"asset_balance_changes": [
{
"asset_type": "credit_alphanum4",
Expand Down Expand Up @@ -121,7 +149,24 @@ func TestInvokeHostFnDetailsInPaymentOperations(t *testing.T) {
tt.Assert.Len(records, 1)

op := records[0].(operations.InvokeHostFunction)
tt.Assert.Equal(op.Function, "fn")
tt.Assert.Equal(len(op.HostFunctions), 3)
tt.Assert.Equal(op.HostFunctions[0].Type, "invoke_contract")
tt.Assert.Equal(len(op.HostFunctions[0].Parameters), 2)
tt.Assert.Equal(op.HostFunctions[0].Parameters[0]["value"], "AAAADwAAAAdmbl9uYW1lAA==")
tt.Assert.Equal(op.HostFunctions[0].Parameters[0]["type"], "Sym")
tt.Assert.Equal(op.HostFunctions[0].Parameters[1]["value"], "AAAAAwAAAAI=")
tt.Assert.Equal(op.HostFunctions[0].Parameters[1]["type"], "U32")

tt.Assert.Equal(op.HostFunctions[1].Type, "create_contract")
tt.Assert.Equal(len(op.HostFunctions[1].Parameters), 2)
tt.Assert.Equal(op.HostFunctions[1].Parameters[0]["from"], "source_account")
tt.Assert.Equal(op.HostFunctions[1].Parameters[0]["type"], "string")
tt.Assert.Equal(op.HostFunctions[1].Parameters[1]["salt"], "123")
tt.Assert.Equal(op.HostFunctions[1].Parameters[1]["type"], "string")

tt.Assert.Equal(op.HostFunctions[2].Type, "upload_wasm")
tt.Assert.Equal(len(op.HostFunctions[2].Parameters), 0)

tt.Assert.Equal(len(op.AssetBalanceChanges), 4)
tt.Assert.Equal(op.AssetBalanceChanges[0].From, "C_CONTRACT_ADDRESS1")
tt.Assert.Equal(op.AssetBalanceChanges[0].To, "G_CLASSIC_ADDRESS1")
Expand Down
63 changes: 36 additions & 27 deletions services/horizon/internal/ingest/processors/operations_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -631,68 +631,77 @@ func (operation *transactionOperationWrapper) Details() (map[string]interface{},
}
case xdr.OperationTypeInvokeHostFunction:
op := operation.operation.Body.MustInvokeHostFunctionOp()
for _, function := range op.Functions {
details["function"] = function.Args.Type.String()
hostFunctions := make([]map[string]interface{}, 0, len(op.Functions))
if balanceChanges, err := operation.parseAssetBalanceChangesFromContractEvents(); err != nil {
return nil, err
} else {
details["asset_balance_changes"] = balanceChanges
}
for _, function := range op.Functions {
hostFunctionInvocation := make(map[string]interface{}, 2)
hostFunctions = append(hostFunctions, hostFunctionInvocation)
params := []map[string]string{}

switch function.Args.Type {
case xdr.HostFunctionTypeHostFunctionTypeInvokeContract:
hostFunctionInvocation["type"] = "invoke_contract"
args := function.Args.MustInvokeContract()
params := make([]map[string]string, 0, len(args))

for _, param := range args {
serializedParam := map[string]string{}
serializedParam["value"] = "n/a"
serializedParam["type"] = "n/a"

if name, ok := param.ArmForSwitch(int32(param.Type)); ok {
serializedParam["type"] = name
if scValTypeName, ok := param.ArmForSwitch(int32(param.Type)); ok {
serializedParam["type"] = scValTypeName
if raw, err := param.MarshalBinary(); err == nil {
serializedParam["value"] = base64.StdEncoding.EncodeToString(raw)
}
}
params = append(params, serializedParam)
}
details["parameters"] = params

if balanceChanges, err := operation.parseAssetBalanceChangesFromContractEvents(); err != nil {
return nil, err
} else {
details["asset_balance_changes"] = balanceChanges
}

case xdr.HostFunctionTypeHostFunctionTypeCreateContract:
hostFunctionInvocation["type"] = "create_contract"
args := function.Args.MustCreateContract()
details["type"] = args.ContractId.Type.String()

switch args.ContractId.Type {
case xdr.ContractIdTypeContractIdFromSourceAccount:
details["salt"] = args.ContractId.MustSalt().String()
params = append(params,
map[string]string{"from": "source_account", "type": "string"},
map[string]string{"salt": args.ContractId.MustSalt().String(), "type": "string"},
)
case xdr.ContractIdTypeContractIdFromEd25519PublicKey:
fromEd25519PublicKey := args.ContractId.MustFromEd25519PublicKey()
details["key"] = xdr.AccountId(xdr.PublicKey{
fromKeyStr := xdr.AccountId(xdr.PublicKey{
Type: xdr.PublicKeyTypePublicKeyTypeEd25519,
Ed25519: &fromEd25519PublicKey.Key,
}).Address()
signature, err := xdr.MarshalBase64(fromEd25519PublicKey.Signature)
if err != nil {
return nil, err
}
details["signature"] = signature
details["salt"] = fromEd25519PublicKey.Salt.String()
params = append(params,
map[string]string{"from": "public_key", "type": "string"},
map[string]string{"key": fromKeyStr, "type": "string"},
map[string]string{"sig": signature, "type": "string"},
map[string]string{"salt": fromEd25519PublicKey.Salt.String(), "type": "string"},
)
case xdr.ContractIdTypeContractIdFromAsset:
details["asset"] = args.ContractId.MustAsset().StringCanonical()
params = append(params,
map[string]string{"from": "asset", "type": "string"},
map[string]string{"asset": args.ContractId.MustAsset().StringCanonical(), "type": "string"},
)
default:
panic(fmt.Errorf("Unknown contract id type: %s", args.ContractId.Type))
panic(fmt.Errorf("unknown contract id type: %s", args.ContractId.Type))
}
details["source"] = args.Executable
case xdr.HostFunctionTypeHostFunctionTypeUploadContractWasm:
args := function.Args.MustUploadContractWasm()
details["code"] = base64.StdEncoding.EncodeToString(args.Code)
hostFunctionInvocation["type"] = "upload_wasm"
default:
panic(fmt.Errorf("Unknown host function type: %s", function.Args.Type))
panic(fmt.Errorf("unknown host function type: %s", function.Args.Type))
}
/*if raw, err := op.Footprint.MarshalBinary(); err == nil {
details["footprint"] = base64.StdEncoding.EncodeToString(raw)
}*/
hostFunctionInvocation["parameters"] = params
}
details["host_functions"] = hostFunctions
default:
panic(fmt.Errorf("Unknown operation type: %s", operation.OperationType()))
}
Expand Down
Loading

0 comments on commit 08e731c

Please sign in to comment.