-
Notifications
You must be signed in to change notification settings - Fork 139
Commit
- Loading branch information
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
access(all) | ||
contract CCF { | ||
/// Encodes an encodable value to CCF. | ||
/// Returns nil if the value cannot be encoded. | ||
access(all) | ||
view fun encode(_ input: &Any): [UInt8]? | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
/* | ||
* Cadence - The resource-oriented smart contract programming language | ||
* | ||
* Copyright Flow Foundation | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package stdlib | ||
|
||
//go:generate go run ../sema/gen -p stdlib ccf.cdc ccf.gen.go | ||
|
||
import ( | ||
"github.com/onflow/cadence" | ||
"github.com/onflow/cadence/common" | ||
"github.com/onflow/cadence/encoding/ccf" | ||
"github.com/onflow/cadence/errors" | ||
"github.com/onflow/cadence/interpreter" | ||
) | ||
|
||
type Exporter interface { | ||
ExportValue( | ||
value interpreter.Value, | ||
interpreter *interpreter.Interpreter, | ||
locationRange interpreter.LocationRange, | ||
) ( | ||
cadence.Value, | ||
error, | ||
) | ||
} | ||
|
||
type CCFContractHandler interface { | ||
Exporter | ||
} | ||
|
||
// newCCFEncodeFunction creates a new host function that encodes a value using the CCF encoding format. | ||
func newCCFEncodeFunction( | ||
gauge common.MemoryGauge, | ||
handler CCFContractHandler, | ||
) *interpreter.HostFunctionValue { | ||
return interpreter.NewStaticHostFunctionValue( | ||
gauge, | ||
CCFTypeEncodeFunctionType, | ||
Check failure on line 53 in stdlib/ccf.go
|
||
func(invocation interpreter.Invocation) interpreter.Value { | ||
inter := invocation.Interpreter | ||
locationRange := invocation.LocationRange | ||
|
||
referenceValue, ok := invocation.Arguments[0].(interpreter.ReferenceValue) | ||
if !ok { | ||
panic(errors.NewUnreachableError()) | ||
} | ||
|
||
referencedValue := referenceValue.ReferencedValue(inter, locationRange, true) | ||
if referencedValue == nil { | ||
return interpreter.Nil | ||
} | ||
|
||
exportedValue, err := handler.ExportValue(*referencedValue, inter, locationRange) | ||
if err != nil { | ||
return interpreter.Nil | ||
} | ||
|
||
encoded, err := ccf.Encode(exportedValue) | ||
if err != nil { | ||
return interpreter.Nil | ||
} | ||
|
||
res := interpreter.ByteSliceToByteArrayValue(inter, encoded) | ||
|
||
return interpreter.NewSomeValueNonCopying(inter, res) | ||
}, | ||
) | ||
} | ||
|
||
var CCFTypeStaticType = interpreter.ConvertSemaToStaticType(nil, CCFType) | ||
Check failure on line 85 in stdlib/ccf.go
|
||
|
||
func NewCCFContract( | ||
gauge common.MemoryGauge, | ||
handler CCFContractHandler, | ||
) StandardLibraryValue { | ||
|
||
ccfContractFields := map[string]interpreter.Value{ | ||
CCFTypeEncodeFunctionName: newCCFEncodeFunction(gauge, handler), | ||
Check failure on line 93 in stdlib/ccf.go
|
||
} | ||
|
||
var ccfContractValue = interpreter.NewSimpleCompositeValue( | ||
gauge, | ||
CCFType.ID(), | ||
Check failure on line 98 in stdlib/ccf.go
|
||
CCFTypeStaticType, | ||
nil, | ||
ccfContractFields, | ||
nil, | ||
nil, | ||
nil, | ||
) | ||
|
||
return StandardLibraryValue{ | ||
Name: CCFTypeName, | ||
Check failure on line 108 in stdlib/ccf.go
|
||
Type: CCFType, | ||
Check failure on line 109 in stdlib/ccf.go
|
||
Value: ccfContractValue, | ||
Kind: common.DeclarationKindContract, | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,106 @@ | ||
/* | ||
* Cadence - The resource-oriented smart contract programming language | ||
* | ||
* Copyright Flow Foundation | ||
* | ||
* Licensed under the Apache License, Version 2.0 (the "License"); | ||
* you may not use this file except in compliance with the License. | ||
* You may obtain a copy of the License at | ||
* | ||
* http://www.apache.org/licenses/LICENSE-2.0 | ||
* | ||
* Unless required by applicable law or agreed to in writing, software | ||
* distributed under the License is distributed on an "AS IS" BASIS, | ||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
* See the License for the specific language governing permissions and | ||
* limitations under the License. | ||
*/ | ||
|
||
package tests | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/onflow/cadence" | ||
"github.com/onflow/cadence/common" | ||
. "github.com/onflow/cadence/runtime" | ||
. "github.com/onflow/cadence/tests/runtime_utils" | ||
) | ||
|
||
func TestRuntimeCCFEncodeStruct(t *testing.T) { | ||
|
||
t.Parallel() | ||
|
||
type testCase struct { | ||
name string | ||
value string | ||
output []byte | ||
} | ||
|
||
tests := []testCase{ | ||
{ | ||
name: "String", | ||
value: `"test"`, | ||
output: []byte{0xd8, 0x82, 0x82, 0xd8, 0x89, 0x1, 0x64, 0x74, 0x65, 0x73, 0x74}, | ||
}, | ||
{ | ||
name: "Bool", | ||
value: `true`, | ||
output: []byte{0xd8, 0x82, 0x82, 0xd8, 0x89, 0x0, 0xf5}, | ||
}, | ||
{ | ||
name: "function", | ||
value: `fun (): Int { return 1 }`, | ||
output: []byte{0xd8, 0x82, 0x82, 0xd8, 0x89, 0x18, 0x33, 0x84, 0x80, 0x80, 0xd8, 0xb9, 0x4, 0x0}, | ||
}, | ||
} | ||
|
||
test := func(test testCase) { | ||
t.Run(test.name, func(t *testing.T) { | ||
|
||
t.Parallel() | ||
|
||
runtime := NewTestInterpreterRuntime() | ||
|
||
runtimeInterface := &TestRuntimeInterface{ | ||
Storage: NewTestLedger(nil, nil), | ||
} | ||
|
||
script := []byte(fmt.Sprintf( | ||
` | ||
access(all) fun main(): [UInt8]? { | ||
let value = %s | ||
return CCF.encode(&value as &AnyStruct) | ||
} | ||
`, | ||
test.value, | ||
)) | ||
|
||
result, err := runtime.ExecuteScript( | ||
Script{ | ||
Source: script, | ||
}, | ||
Context{ | ||
Interface: runtimeInterface, | ||
Location: common.ScriptLocation{}, | ||
}, | ||
) | ||
require.NoError(t, err) | ||
|
||
assert.Equal(t, | ||
cadence.NewOptional( | ||
cadence.ByteSliceToByteArray(test.output), | ||
), | ||
result, | ||
) | ||
}) | ||
} | ||
|
||
for _, testCase := range tests { | ||
test(testCase) | ||
} | ||
} |