From a703b8b3aa7dd447cb9688a3996eff1324429f5a Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Wed, 16 Mar 2022 21:17:26 +0200 Subject: [PATCH 1/2] Handle strings in binary codec. --- src/smartcontracts/codec/binary.spec.ts | 20 ++++++++++++++++- src/smartcontracts/codec/primitive.ts | 8 +++++++ src/smartcontracts/codec/string.ts | 26 +++++++++++++++++++++++ src/smartcontracts/typesystem/matchers.ts | 9 ++++++++ 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/smartcontracts/codec/string.ts diff --git a/src/smartcontracts/codec/binary.spec.ts b/src/smartcontracts/codec/binary.spec.ts index cdb3c77b..b7dbf9b1 100644 --- a/src/smartcontracts/codec/binary.spec.ts +++ b/src/smartcontracts/codec/binary.spec.ts @@ -1,6 +1,6 @@ import { assert } from "chai"; import { BinaryCodec, BinaryCodecConstraints } from "./binary"; -import { AddressType, AddressValue, BigIntType, BigUIntType, BigUIntValue, BooleanType, BooleanValue, I16Type, I32Type, I64Type, I8Type, NumericalType, NumericalValue, Struct, Field, StructType, TypedValue, U16Type, U32Type, U32Value, U64Type, U64Value, U8Type, U8Value, List, ListType, EnumType, EnumVariantDefinition, EnumValue, ArrayVec, ArrayVecType, U16Value, TokenIdentifierType, TokenIdentifierValue } from "../typesystem"; +import { AddressType, AddressValue, BigIntType, BigUIntType, BigUIntValue, BooleanType, BooleanValue, I16Type, I32Type, I64Type, I8Type, NumericalType, NumericalValue, Struct, Field, StructType, TypedValue, U16Type, U32Type, U32Value, U64Type, U64Value, U8Type, U8Value, List, ListType, EnumType, EnumVariantDefinition, EnumValue, ArrayVec, ArrayVecType, U16Value, TokenIdentifierType, TokenIdentifierValue, StringValue, StringType } from "../typesystem"; import { discardSuperfluousBytesInTwosComplement, discardSuperfluousZeroBytes, isMsbOne } from "./utils"; import { Address } from "../../address"; import { Balance } from "../../balance"; @@ -84,6 +84,24 @@ describe("test binary codec (basic)", () => { assert.isTrue(decodedTop.equals(value)); } }); + + it("should create bytes and strings, encode and decode", async () => { + let bytesValue = BytesValue.fromHex("74657374"); + let stringValue = StringValue.fromHex("74657374"); + + let length = [0x00, 0x00, 0x00, 0x04]; + let payload = [0x74, 0x65, 0x73, 0x74]; + + assert.deepEqual(codec.encodeNested(bytesValue), Buffer.from([...length, ...payload])); + assert.deepEqual(codec.encodeTopLevel(bytesValue), Buffer.from(payload)); + assert.deepEqual(codec.decodeNested(Buffer.from([...length, ...payload]), new BytesType()), [bytesValue, 8]); + assert.deepEqual(codec.decodeTopLevel(Buffer.from(payload), new BytesType()), bytesValue); + + assert.deepEqual(codec.encodeNested(stringValue), Buffer.from([...length, ...payload])); + assert.deepEqual(codec.encodeTopLevel(stringValue), Buffer.from(payload)); + assert.deepEqual(codec.decodeNested(Buffer.from([...length, ...payload]), new StringType()), [stringValue, 8]); + assert.deepEqual(codec.decodeTopLevel(Buffer.from(payload), new StringType()), stringValue); + }); }); describe("test binary codec (advanced)", () => { diff --git a/src/smartcontracts/codec/primitive.ts b/src/smartcontracts/codec/primitive.ts index 6e176f87..833b9e14 100644 --- a/src/smartcontracts/codec/primitive.ts +++ b/src/smartcontracts/codec/primitive.ts @@ -7,6 +7,7 @@ import { BooleanValue, NumericalValue, AddressValue, + StringValue, } from "../typesystem"; import { AddressBinaryCodec } from "./address"; import { BooleanBinaryCodec } from "./boolean"; @@ -19,6 +20,7 @@ import { BytesBinaryCodec } from "./bytes"; import { TokenIdentifierCodec } from "./tokenIdentifier"; import { TokenIdentifierValue } from "../typesystem/tokenIdentifier"; import { NothingCodec } from "./nothing"; +import { StringBinaryCodec } from "./string"; export class PrimitiveBinaryCodec { private readonly binaryCodec: BinaryCodec; @@ -28,6 +30,7 @@ export class PrimitiveBinaryCodec { private readonly addressCodec: AddressBinaryCodec; private readonly h256Codec: H256BinaryCodec; private readonly bytesCodec: BytesBinaryCodec; + private readonly stringCodec: StringBinaryCodec; private readonly tokenIdentifierCodec: TokenIdentifierCodec; private readonly nothingCodec: NothingCodec; @@ -39,6 +42,7 @@ export class PrimitiveBinaryCodec { this.addressCodec = new AddressBinaryCodec(); this.h256Codec = new H256BinaryCodec(); this.bytesCodec = new BytesBinaryCodec(); + this.stringCodec = new StringBinaryCodec(); this.tokenIdentifierCodec = new TokenIdentifierCodec(); this.nothingCodec = new NothingCodec(); } @@ -49,6 +53,7 @@ export class PrimitiveBinaryCodec { onNumerical: () => this.numericalCodec.decodeNested(buffer, type), onAddress: () => this.addressCodec.decodeNested(buffer), onBytes: () => this.bytesCodec.decodeNested(buffer), + onString: () => this.stringCodec.decodeNested(buffer), onH256: () => this.h256Codec.decodeNested(buffer), onTokenIndetifier: () => this.tokenIdentifierCodec.decodeNested(buffer), onNothing: () => this.nothingCodec.decodeNested() @@ -61,6 +66,7 @@ export class PrimitiveBinaryCodec { onNumerical: () => this.numericalCodec.decodeTopLevel(buffer, type), onAddress: () => this.addressCodec.decodeTopLevel(buffer), onBytes: () => this.bytesCodec.decodeTopLevel(buffer), + onString: () => this.stringCodec.decodeTopLevel(buffer), onH256: () => this.h256Codec.decodeTopLevel(buffer), onTokenIndetifier: () => this.tokenIdentifierCodec.decodeTopLevel(buffer), onNothing: () => this.nothingCodec.decodeTopLevel() @@ -73,6 +79,7 @@ export class PrimitiveBinaryCodec { onNumerical: () => this.numericalCodec.encodeNested(value), onAddress: () => this.addressCodec.encodeNested(value), onBytes: () => this.bytesCodec.encodeNested(value), + onString: () => this.stringCodec.encodeNested(value), onH256: () => this.h256Codec.encodeNested(value), onTypeIdentifier: () => this.tokenIdentifierCodec.encodeNested(value), onNothing: () => this.nothingCodec.encodeNested() @@ -85,6 +92,7 @@ export class PrimitiveBinaryCodec { onNumerical: () => this.numericalCodec.encodeTopLevel(value), onAddress: () => this.addressCodec.encodeTopLevel(value), onBytes: () => this.bytesCodec.encodeTopLevel(value), + onString: () => this.stringCodec.encodeTopLevel(value), onH256: () => this.h256Codec.encodeTopLevel(value), onTypeIdentifier: () => this.tokenIdentifierCodec.encodeTopLevel(value), onNothing: () => this.nothingCodec.encodeTopLevel() diff --git a/src/smartcontracts/codec/string.ts b/src/smartcontracts/codec/string.ts new file mode 100644 index 00000000..2f6db519 --- /dev/null +++ b/src/smartcontracts/codec/string.ts @@ -0,0 +1,26 @@ +import { StringValue } from "../typesystem"; +import { BytesValue } from "../typesystem/bytes"; +import { BytesBinaryCodec } from "./bytes"; + +export class StringBinaryCodec { + private readonly bytesBinaryCodec = new BytesBinaryCodec(); + + decodeNested(buffer: Buffer): [StringValue, number] { + let [decoded, length] = this.bytesBinaryCodec.decodeNested(buffer); + let decodedAsString = new StringValue(decoded.valueOf().toString()); + return [decodedAsString, length]; + } + + decodeTopLevel(buffer: Buffer): StringValue { + return new StringValue(buffer.toString()); + } + + encodeNested(value: StringValue): Buffer { + let valueAsBytes = BytesValue.fromUTF8(value.valueOf()); + return this.bytesBinaryCodec.encodeNested(valueAsBytes); + } + + encodeTopLevel(value: StringValue): Buffer { + return Buffer.from(value.valueOf()); + } +} diff --git a/src/smartcontracts/typesystem/matchers.ts b/src/smartcontracts/typesystem/matchers.ts index 3dc343d7..a673cd23 100644 --- a/src/smartcontracts/typesystem/matchers.ts +++ b/src/smartcontracts/typesystem/matchers.ts @@ -13,6 +13,7 @@ import { Tuple, TupleType } from "./tuple"; import { Type, PrimitiveType, PrimitiveValue } from "./types"; import { ArrayVec, ArrayVecType } from "./genericArray"; import { TypedValue } from "./types"; +import { StringType, StringValue } from "./string"; // TODO: Extend functionality or rename wrt. restricted / reduced functionality (not all types are handled: composite, variadic). export function onTypeSelect( @@ -106,6 +107,7 @@ export function onPrimitiveValueSelect( onNumerical: () => TResult; onAddress: () => TResult; onBytes: () => TResult; + onString: () => TResult; onH256: () => TResult; onTypeIdentifier: () => TResult; onNothing: () => TResult; @@ -124,6 +126,9 @@ export function onPrimitiveValueSelect( if (value.hasJavascriptConstructorInHierarchy(BytesValue.name)) { return selectors.onBytes(); } + if (value.hasJavascriptConstructorInHierarchy(StringValue.name)) { + return selectors.onString(); + } if (value.hasJavascriptConstructorInHierarchy(H256Value.name)) { return selectors.onH256(); } @@ -147,6 +152,7 @@ export function onPrimitiveTypeSelect( onNumerical: () => TResult; onAddress: () => TResult; onBytes: () => TResult; + onString: () => TResult; onH256: () => TResult; onTokenIndetifier: () => TResult; onNothing: () => TResult; @@ -165,6 +171,9 @@ export function onPrimitiveTypeSelect( if (type.hasJavascriptConstructorInHierarchy(BytesType.name)) { return selectors.onBytes(); } + if (type.hasJavascriptConstructorInHierarchy(StringType.name)) { + return selectors.onString(); + } if (type.hasJavascriptConstructorInHierarchy(H256Type.name)) { return selectors.onH256(); } From b97dfd5d1fedba5c947b4d81bd341a4af59c3219 Mon Sep 17 00:00:00 2001 From: Andrei Bancioiu Date: Wed, 16 Mar 2022 21:21:58 +0200 Subject: [PATCH 2/2] Update changelog. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index c9c8bedb..859ef0b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,8 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how ## Unreleased - [Fix / simplify some imports](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/148) + - [Handle strings in binary codec](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/153) + - [Fix data fetching from the blockchain - wait before polling for the first time](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/152) ## [9.2.0] - [Fix WalletProvider URL when nonce = 0](https://github.com/ElrondNetwork/elrond-sdk-erdjs/pull/137)