Skip to content

Commit

Permalink
Merge pull request #24 from Chia-Mine/v1.0.2
Browse files Browse the repository at this point in the history
V1.0.2
  • Loading branch information
ChiaMineJP authored Aug 3, 2021
2 parents e6ccb30 + 006d380 commit 277a2c0
Show file tree
Hide file tree
Showing 10 changed files with 241 additions and 57 deletions.
11 changes: 11 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,15 @@
# Changelog

## [1.0.2]
This version is compatible with [`1a5cb17895d8707f784a85180bc97d3c6ebe71a0`](https://github.com/Chia-Network/clvm/tree/1a5cb17895d8707f784a85180bc97d3c6ebe71a0) of [[email protected]](https://github.com/Chia-Network/clvm)

### Fixed
- Fixed an issue where `op_pubkey_for_exp` was missing.
- Fixed an issue where arithmetic op of atoms larger than 32 bit integer did not work.
### Added
- Added `bigint_to_bytes` function in `src/casts.ts`
- Added tests for Bytes-BigInt conversion.

## [1.0.1]
This version is compatible with [`1a5cb17895d8707f784a85180bc97d3c6ebe71a0`](https://github.com/Chia-Network/clvm/tree/1a5cb17895d8707f784a85180bc97d3c6ebe71a0) of [[email protected]](https://github.com/Chia-Network/clvm)

Expand Down Expand Up @@ -170,6 +180,7 @@ At this version, I've managed to improve test complete time to `79s` -> `2s` by
Initial (beta) release.

<!--[Unreleased]: https://github.com/Chia-Mine/clvm-js/compare/v0.0.1...v0.0.2-->
[1.0.2]: https://github.com/Chia-Mine/clvm-js/compare/v1.0.1...v1.0.2
[1.0.1]: https://github.com/Chia-Mine/clvm-js/compare/v1.0.0...v1.0.1
[1.0.0]: https://github.com/Chia-Mine/clvm-js/compare/v0.0.19...v1.0.0
[0.0.19]: https://github.com/Chia-Mine/clvm-js/compare/v0.0.18...v0.0.19
Expand Down
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,11 @@ const tuple2 = t(1, 2, 3);
tuple2; // (1, 2)

// You can check if a variable is a tuple
const {isTuple} = require("clvm");
const {isTuple, isList} = require("clvm");
isTuple([1, 2]); // false
isTuple(t(1, 2)); // true
isList([1, 2]); // true
isList(t(1, 2)); // false
```
Just add `t` in front of tuple parenthesis `(1, 2)` and you get a tuple.

Expand Down Expand Up @@ -133,6 +135,12 @@ b2 = new Bytes(uint8);
b2.equal_to(b("abc")); // true
uint8[0] = 0;
b2.at(0); // 0

// Check byte instance
const {isBytes} = require("clvm");
isBytes(b('aaa')); // true
isBytes(h('0xfe')); // true
isBytes(new Bytes()); // true
```

### Python's `str(x)` is `x.toString()` in Javascript
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "clvm",
"version": "1.0.1",
"version": "1.0.2",
"author": "Admin ChiaMineJP <[email protected]>",
"description": "Javascript implementation of chia lisp",
"license": "MIT",
Expand Down
10 changes: 9 additions & 1 deletion src/SExp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {G1Element} from "@chiamine/bls-signatures";
import {None, Optional} from "./__python_types__";
import {CLVMObject, CLVMType} from "./CLVMObject";
import {Bytes, isIterable, Tuple, t, Stream, isBytes, isTuple} from "./__type_compatibility__";
import {int_from_bytes, int_to_bytes} from "./casts";
import {bigint_from_bytes, bigint_to_bytes, int_from_bytes, int_to_bytes} from "./casts";
import {sexp_to_stream} from "./serialize";
import {as_javascript} from "./as_javascript";
import {EvalError} from "./EvalError";
Expand All @@ -12,6 +12,7 @@ export type CastableType = SExp
| Bytes
| string
| number
| bigint
| None
| G1Element
| CastableType[]
Expand Down Expand Up @@ -40,6 +41,9 @@ export function convert_atom_to_bytes(v: any): Bytes {
else if(typeof v === "boolean"){ // Tips. In Python, isinstance(True, int) == True.
return int_to_bytes(v ? 1 : 0);
}
else if(typeof v === "bigint"){
return bigint_to_bytes(v);
}
else if(v === None || !v){
return Bytes.NULL;
}
Expand Down Expand Up @@ -217,6 +221,10 @@ export class SExp implements CLVMType {
return int_from_bytes(this.atom);
}

public as_bigint(){
return bigint_from_bytes(this.atom);
}

public as_bin(){
const f = new Stream();
sexp_to_stream(this, f);
Expand Down
3 changes: 3 additions & 0 deletions src/__type_compatibility__.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,9 @@ export class Bytes {
return new Bytes(Uint8Array.from(value));
}
else if(typeof value === "string"){
if(!value){
return new Bytes();
}
if(type === "hex"){
value = value.replace(/^0x/, "");
return new Bytes(Hex.parse(value).toUint8Array());
Expand Down
85 changes: 69 additions & 16 deletions src/casts.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,37 @@
import {None} from "./__python_types__";
import {Bytes, h} from "./__type_compatibility__";
import {Bytes} from "./__type_compatibility__";

// In javascript, max safe integer is 2**53-1 (53bit)
// Surprisingly, parseInt() can parse over 32bit integer.
export function int_from_bytes(b: Bytes|None): number {
if(!b || b.length === 0){
return 0;
}
const unsigned32 = parseInt(b.hex(), 16);
else if(b.length*8 > 52){
throw new Error("Cannot convert Bytes to Integer larger than 52bit. Use bigint_from_bytes instead.");
}
let unsigned32 = 0;
for(let i=b.length-1;i>=0;i--){
const byte = b.at(i);
unsigned32 += byte * (256**((b.length-1)-i));
}
// If the first bit is 1, it is recognized as a negative number.
if(b.at(0) & 0x80){
return unsigned32 - (256**b.length);
}
return unsigned32;
}

export function bigint_from_bytes(b: Bytes|None): bigint {
if(!b || b.length === 0){
return BigInt(0);
}
let unsigned32 = BigInt(0);
for(let i=b.length-1;i>=0;i--){
const byte = b.at(i);
unsigned32 += BigInt(byte) * (BigInt(256)**(BigInt((b.length-1)-i)));
}
// If the first bit is 1, it is recognized as a negative number.
if(b.at(0) & 0x80){
return unsigned32 - (1 << (b.length*8));
return unsigned32 - (BigInt(1) << BigInt(b.length*8));
}
return unsigned32;
}
Expand All @@ -23,26 +44,58 @@ export function int_to_bytes(v: number): Bytes {
return Bytes.NULL;
}

const byte_count = ((v < 0 ? -v : v).toString(2).length + 8) >> 3;
let hexStr = (v >>> 0).toString(16);
if(v >= 0){
hexStr = hexStr.length % 2 ? `0${hexStr}` : hexStr;
let byte_count = 1;
if(v > 0){
while(2**(8*byte_count - 1) - 1 < v){
byte_count++;
}
}
else if(v < 0){
while(2**(8*byte_count - 1) < -v){
byte_count++;
}
}

const needExtraByte = v > 0 && ((v >> ((byte_count-1)*8)) & 0x80) > 0;
const u8 = new Uint8Array(byte_count+(needExtraByte ? 1 : 0));
for(let i=0;i<byte_count;i++){
const j = needExtraByte ? i+1 : i;
u8[j] = (v >> (byte_count-i-1)*8) & 0xff;
}

return new Bytes(u8);
}

export function bigint_to_bytes(v: bigint): Bytes {
if(v === BigInt(0)){
return Bytes.NULL;
}
let byte_count = 1;
if(v > 0){
while(BigInt(2)**(BigInt(8)*BigInt(byte_count) - BigInt(1)) - BigInt(1) < v){
byte_count++;
}
}
while(hexStr.length / 2 < byte_count){
hexStr = "00" + hexStr;
else if(v < 0){
while(BigInt(2)**(BigInt(8)*BigInt(byte_count) - BigInt(1)) < -v){
byte_count++;
}
}

while(hexStr.length > 2 && hexStr.substr(0, 2) === (parseInt(hexStr.substr(2, 2), 16) & 0x80 ? "ff" : "00")){
hexStr = hexStr.substr(2);
const needExtraByte = v > 0 && ((v >> (BigInt(byte_count-1)*BigInt(8))) & BigInt(0x80)) > BigInt(0);
const u8 = new Uint8Array(byte_count+(needExtraByte ? 1 : 0));
for(let i=0;i<byte_count;i++){
const j = needExtraByte ? i+1 : i;
u8[j] = Number((v >> (BigInt(byte_count-i-1))*BigInt(8)) & BigInt(0xff));
}

return h(hexStr);
return new Bytes(u8);
}

/**
* Return the number of bytes required to represent this integer.
* @param {number} v
*/
export function limbs_for_int(v: number): number {
return ((v < 0 ? -v : v).toString(2).length + 7) >> 3;
export function limbs_for_int(v: number|bigint): number {
return ((v >= 0 ? v : -v).toString(2).length + 7) >> 3;
}
Loading

0 comments on commit 277a2c0

Please sign in to comment.