All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.klaytn.caver.account.AccountKeyWeightedMultiSig Maven / Gradle / Ivy

There is a newer version: 1.12.2-android
Show newest version
/*
 * Copyright 2020 The caver-java Authors
 *
 * 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 com.klaytn.caver.account;

import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.klaytn.caver.utils.BytesUtils;
import com.klaytn.caver.utils.Utils;
import org.web3j.protocol.ObjectMapperFactory;
import org.web3j.rlp.*;
import org.web3j.utils.Numeric;

import java.io.IOException;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;


/**
 * AccountKeyWeightedMultiSig is an account key type containing a threshold and WeightedPublicKeys.
 * WeightedPublicKeys contains a slice of {weight and key}. To be a valid tx for an account associated
 * with AccountKeyWeightedMultiSig, the weighted sum of signed public keys should be larger than the threshold.
 */
@JsonDeserialize(using = AccountKeyWeightedMultiSig.AccountKeyWeightedMultiSigDeserializer.class)
@JsonSerialize(using = AccountKeyWeightedMultiSig.AccountKeyWeightedMultiSigSerializer.class)
public class AccountKeyWeightedMultiSig implements IAccountKey {

    /**
     * AccountKeyWeightedMultiSig's Type attribute.
     */
    private static final String TYPE = "0x04";

    /**
     * Validation threshold. To be a valid transaction, the weight sum of signatures should be larger than
     * or equal to the threshold.
     */
    private BigInteger threshold;

    /**
     * A List of weighted public keys. A weighted public key contains a weight and a public key.
     */
    private List weightedPublicKeys;


    /**
     * Creates AccountKeyWeightedMultiSig instance with threshold, weightedPublicKey List
     * @param threshold Threshold of AccountKeyWeightedMultiSig
     * @param weightedPublicKeys WeightedPublicKey List
     * @return AccountKeyWeightedMultiSig
     */
    private AccountKeyWeightedMultiSig(BigInteger threshold, List weightedPublicKeys) {
        this.threshold = threshold;
        this.weightedPublicKeys = weightedPublicKeys;
    }

    /**
     * Decodes a RLP-encoded AccountKeyWeightedMultiSig string
     * @param rlpEncodedKey RLP-encoded AccountKeyWeightedMultiSig string.
     * @return AccountKeyWeightedMultiSig
     */
    public static AccountKeyWeightedMultiSig decode(String rlpEncodedKey) {
        return AccountKeyWeightedMultiSig.decode(Numeric.hexStringToByteArray(rlpEncodedKey));
    }

    /**
     * Decodes a RLP-encoded AccountKeyWeightedMultiSig byte array.
     * @param rlpEncodedKey RLP-encoded AccountKeyWeightedMultiSig byte array.
     * @return AccountKeyWeightedMultiSig
     */
    public static AccountKeyWeightedMultiSig decode(byte[] rlpEncodedKey) {
        //check tag
        byte type = Numeric.hexStringToByteArray(TYPE)[0];
        if(rlpEncodedKey[0] != type) {
            throw new IllegalArgumentException("Invalid RLP-encoded AccountKeyWeightedMultiSig Tag");
        }

        //remove Tag
        byte[] encodedAccountKey = Arrays.copyOfRange(rlpEncodedKey, 1, rlpEncodedKey.length);
        RlpList rlpList = RlpDecoder.decode(encodedAccountKey);
        RlpList values = (RlpList)rlpList.getValues().get(0);

        //get threshold
        BigInteger threshold = ((RlpString)values.getValues().get(0)).asPositiveBigInteger();

        //get WeightedPublicKey
        List weightedPublicKeys = new ArrayList<>();
        RlpList rlpWeightedPublicKeys = (RlpList) values.getValues().get(1);
        for (RlpType item : rlpWeightedPublicKeys.getValues()) {
            RlpList rlpWeightedPublicKey = (RlpList) item;
            BigInteger weight = ((RlpString) rlpWeightedPublicKey.getValues().get(0)).asPositiveBigInteger();
            String compressedPublicKey = ((RlpString) rlpWeightedPublicKey.getValues().get(1)).asString();
            weightedPublicKeys.add(new WeightedPublicKey(compressedPublicKey, weight));
        }

        return new AccountKeyWeightedMultiSig(threshold, weightedPublicKeys);
    }

    /**
     * Create AccountKeyWeightedMultiSig instance form public Key array and WeightedMultiSigOption
     * @param publicKeyArr Array of public key string
     * @param options An options which defines threshold and weight.
     * @return AccountKeyWeightedMultiSig
     */
    public static AccountKeyWeightedMultiSig fromPublicKeysAndOptions(String[] publicKeyArr, WeightedMultiSigOptions options) {
        if(publicKeyArr.length > WeightedMultiSigOptions.MAX_COUNT_WEIGHTED_PUBLIC_KEY) {
            throw new IllegalArgumentException("It exceeds maximum public key count.");
        }

        if(publicKeyArr.length != options.getWeights().size()) {
            throw new IllegalArgumentException("The count of public keys is not equal to the length of weight array.");
        }

        List weightedPublicKeyList = new ArrayList<>();
        for(int i=0; i< publicKeyArr.length; i++) {
            weightedPublicKeyList.add(new WeightedPublicKey(publicKeyArr[i], options.getWeights().get(i)));
        }

        return new AccountKeyWeightedMultiSig(options.getThreshold(), weightedPublicKeyList);
    }


    /**
     * Encodes a AccountKeyWeightedMultiSig Object by RLP-encoding method.
     * @return RLP-encoded AccountKeyWeightedMultiSig String
     */
    @Override
    public String getRLPEncoding() {
        if (threshold == null || weightedPublicKeys == null) {
            throw new NullPointerException("threshold or weightedPublicKeys must be exists for multisig.");
        }
        if(weightedPublicKeys.size() == 0) {
            throw new RuntimeException("weightedPublicKeys must have items for multisig.");
        }

        List rlpTypeList = new ArrayList<>();

        rlpTypeList.add(RlpString.create(this.threshold));

        List rlpWeightedPublicKeyList = new ArrayList<>();
        for(WeightedPublicKey item : this.weightedPublicKeys) {

            if(item.getPublicKey() == null) {
                throw new RuntimeException("public key should be specified for a multisig account");
            }

            if(item.getWeight() == null) {
                throw new RuntimeException("weight should be specified for a multisig account");
            }

            List rlpWeightedPublicKey = new ArrayList<>();

            BigInteger weight = item.getWeight();
            String compressedKey = Utils.compressPublicKey(item.getPublicKey());
            rlpWeightedPublicKey.addAll(Arrays.asList(
                    RlpString.create(weight),
                    RlpString.create(Numeric.hexStringToByteArray(compressedKey))
            ));

            rlpWeightedPublicKeyList.add(new RlpList(rlpWeightedPublicKey));
        }
        rlpTypeList.add(new RlpList(rlpWeightedPublicKeyList));

        byte[] encodedWeightedKey = RlpEncoder.encode(new RlpList(rlpTypeList));
        byte[] type = Numeric.hexStringToByteArray(AccountKeyWeightedMultiSig.getType());

        return Numeric.toHexString(BytesUtils.concat(type, encodedWeightedKey));
    }

    /**
     * Getter function for threshold
     * @return threshold
     */
    public BigInteger getThreshold() {
        return threshold;
    }

    /**
     * Getter function for List of WeightedOfPublicKey
     * @return List of WeightedOfPublicKey
     */
    public List getWeightedPublicKeys() {
        return weightedPublicKeys;
    }

    /**
     * Returns an AccountKeyWeightedMultiSig's type attribute
     * @return AccountKeyWeightedMultiSig's type attribute
     */
    public static String getType() {
        return TYPE;
    }

    /**
     * Serialize class to AccountKeyWeightedMultiSig into JSON.
     */
    public static class AccountKeyWeightedMultiSigSerializer extends JsonSerializer {
        @Override
        public void serialize(AccountKeyWeightedMultiSig accountKeyWeightedMultiSig, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
            jsonGenerator.writeStartObject(); // Start Object

            jsonGenerator.writeFieldName("keyType");
            jsonGenerator.writeNumber(Numeric.toBigInt(getType()));

            jsonGenerator.writeObjectFieldStart("key");

            jsonGenerator.writeFieldName("threshold");
            jsonGenerator.writeNumber(accountKeyWeightedMultiSig.getThreshold());

            jsonGenerator.writeArrayFieldStart("keys");
            for(WeightedPublicKey weightedPublicKey : accountKeyWeightedMultiSig.getWeightedPublicKeys()) {
                jsonGenerator.writeObject(weightedPublicKey);
            }
            jsonGenerator.writeEndArray(); //end of keys array

            jsonGenerator.writeEndObject(); // end of key

            jsonGenerator.writeEndObject(); // End Object
        }
    }

    /**
     * Deserialize class to JSON to AccountKeyWeightedMultiSig.
     */
    public static class AccountKeyWeightedMultiSigDeserializer extends JsonDeserializer {

        private static ObjectMapper objectMapper = ObjectMapperFactory.getObjectMapper();

        @Override
        public AccountKeyWeightedMultiSig deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JsonProcessingException {
            JsonNode node = p.getCodec().readTree(p);
            byte type = (byte)node.get("keyType").intValue();

            JsonNode key = node.get("key");
            BigInteger threshold = key.get("threshold").bigIntegerValue();

            JsonNode keys = key.get("keys");
            Iterator iterator = keys.iterator();
            List weightedPublicKeyList = new ArrayList<>();

            while(iterator.hasNext()) {
                JsonNode jsonNode = iterator.next();
                WeightedPublicKey weightedPublicKey = (WeightedPublicKey) objectMapper.readValue(jsonNode.toString(), WeightedPublicKey.class);
                weightedPublicKeyList.add(weightedPublicKey);
            }

            return new AccountKeyWeightedMultiSig(threshold, weightedPublicKeyList);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy